|
a |
|
b/ants/segmentation/atropos.py |
|
|
1 |
""" |
|
|
2 |
Atropos segmentation |
|
|
3 |
""" |
|
|
4 |
|
|
|
5 |
|
|
|
6 |
|
|
|
7 |
__all__ = ['atropos'] |
|
|
8 |
|
|
|
9 |
import os |
|
|
10 |
import glob |
|
|
11 |
import warnings |
|
|
12 |
from tempfile import mktemp |
|
|
13 |
|
|
|
14 |
|
|
|
15 |
import ants |
|
|
16 |
from ants.internal import get_lib_fn, get_pointer_string, process_arguments |
|
|
17 |
|
|
|
18 |
|
|
|
19 |
def atropos(a, x, i='Kmeans[3]', m='[0.2,1x1]', c='[5,0]', |
|
|
20 |
priorweight=0.25, **kwargs): |
|
|
21 |
""" |
|
|
22 |
A finite mixture modeling (FMM) segmentation approach with possibilities |
|
|
23 |
for specifying prior constraints. These prior constraints include the |
|
|
24 |
specification of a prior label image, prior probability images (one for |
|
|
25 |
each class), and/or an MRF prior to enforce spatial smoothing of the |
|
|
26 |
labels. Similar algorithms include FAST and SPM. atropos can also perform |
|
|
27 |
multivariate segmentation if you pass a list of images in: e.g. a=(img1,img2). |
|
|
28 |
|
|
|
29 |
ANTsR function: `atropos` |
|
|
30 |
|
|
|
31 |
Arguments |
|
|
32 |
--------- |
|
|
33 |
a : ANTsImage or list/tuple of ANTsImage types |
|
|
34 |
One or more scalar images to segment. If priors are not used, |
|
|
35 |
the intensities of the first image are used to order the classes |
|
|
36 |
in the segmentation output, from lowest to highest intensity. Otherwise |
|
|
37 |
the order of the classes is dictated by the order of the prior images. |
|
|
38 |
|
|
|
39 |
x : ANTsImage |
|
|
40 |
mask image. |
|
|
41 |
|
|
|
42 |
i : string |
|
|
43 |
initialization usually KMeans[N] for N classes or a list of N prior |
|
|
44 |
probability images. See Atropos in ANTs for full set of options. |
|
|
45 |
|
|
|
46 |
m : string |
|
|
47 |
mrf parameters as a string, usually "[smoothingFactor,radius]" where |
|
|
48 |
smoothingFactor determines the amount of smoothing and radius determines |
|
|
49 |
the MRF neighborhood, as an ANTs style neighborhood vector eg "1x1x1" |
|
|
50 |
for a 3D image. The radius must match the dimensionality of the image, |
|
|
51 |
eg 1x1 for 2D and The default in ANTs is smoothingFactor=0.3 and |
|
|
52 |
radius=1. See Atropos for more options. |
|
|
53 |
|
|
|
54 |
c : string |
|
|
55 |
convergence parameters, "[numberOfIterations,convergenceThreshold]". |
|
|
56 |
A threshold of 0 runs the full numberOfIterations, otherwise Atropos |
|
|
57 |
tests convergence by comparing the mean maximum posterior probability |
|
|
58 |
over the whole region of interest defined by the mask x. |
|
|
59 |
|
|
|
60 |
priorweight : scalar |
|
|
61 |
usually 0 (priors used for initialization only), 0.25 or 0.5. |
|
|
62 |
|
|
|
63 |
kwargs : keyword arguments |
|
|
64 |
more parameters, see Atropos help in ANTs |
|
|
65 |
|
|
|
66 |
Returns |
|
|
67 |
------- |
|
|
68 |
dictionary with the following key/value pairs: |
|
|
69 |
`segmentation`: ANTsImage |
|
|
70 |
actually segmented image |
|
|
71 |
`probabilityimages` : list of ANTsImage types |
|
|
72 |
one image for each segmentation class |
|
|
73 |
|
|
|
74 |
Example |
|
|
75 |
------- |
|
|
76 |
>>> import ants |
|
|
77 |
>>> img = ants.image_read(ants.get_ants_data('r16')) |
|
|
78 |
>>> img = ants.resample_image(img, (64,64), 1, 0) |
|
|
79 |
>>> mask = ants.get_mask(img) |
|
|
80 |
>>> ants.atropos( a = img, m = '[0.2,1x1]', c = '[2,0]', i = 'kmeans[3]', x = mask ) |
|
|
81 |
>>> seg2 = ants.atropos( a = img, m = '[0.2,1x1]', c = '[2,0]', i = seg['probabilityimages'], x = mask, priorweight=0.25 ) |
|
|
82 |
""" |
|
|
83 |
probs = mktemp(prefix='antsr', suffix='prob%02d.nii.gz') |
|
|
84 |
tdir = probs.replace(os.path.basename(probs),'') |
|
|
85 |
probsbase = os.path.basename(probs) |
|
|
86 |
searchpattern = probsbase.replace('%02d', '*') |
|
|
87 |
|
|
|
88 |
ct = 0 |
|
|
89 |
if isinstance(i, (list,tuple)) and (len(i) > 1): |
|
|
90 |
while ct < len(i): |
|
|
91 |
probchar = str(ct+1) |
|
|
92 |
if ct < 9: |
|
|
93 |
probchar = '0%s' % probchar |
|
|
94 |
tempfn = probs.replace('%02d', probchar) |
|
|
95 |
ants.image_write(i[ct], tempfn) |
|
|
96 |
ct += 1 |
|
|
97 |
i = 'PriorProbabilityImages[%s,%s,%s]' % (str(len(i)), probs, str(priorweight)) |
|
|
98 |
|
|
|
99 |
if isinstance(a, (list,tuple)): |
|
|
100 |
outimg = a[0].clone('unsigned int') |
|
|
101 |
else: |
|
|
102 |
outimg = a.clone('unsigned int') |
|
|
103 |
|
|
|
104 |
mydim = outimg.dimension |
|
|
105 |
outs = '[%s,%s]' % (get_pointer_string(outimg), probs) |
|
|
106 |
mymask = x.clone('unsigned int') |
|
|
107 |
|
|
|
108 |
if (not isinstance(a, (list,tuple))) or (len(a) == 1): |
|
|
109 |
myargs = { |
|
|
110 |
'd': mydim, |
|
|
111 |
'a': a, |
|
|
112 |
'm-MULTINAME-0': m, |
|
|
113 |
'o': outs, |
|
|
114 |
'c': c, |
|
|
115 |
'm-MULTINAME-1': m, |
|
|
116 |
'i': i, |
|
|
117 |
'x': mymask |
|
|
118 |
} |
|
|
119 |
for k, v in kwargs.items(): |
|
|
120 |
myargs[k] = v |
|
|
121 |
|
|
|
122 |
elif isinstance(a, (list, tuple)): |
|
|
123 |
if len(a) > 6: |
|
|
124 |
print('more than 6 input images not really supported, using first 6') |
|
|
125 |
a = a[:6] |
|
|
126 |
myargs = { |
|
|
127 |
'd': mydim, |
|
|
128 |
'm-MULTINAME-0': m, |
|
|
129 |
'o': outs, |
|
|
130 |
'c': c, |
|
|
131 |
'm-MULTINAME-1': m, |
|
|
132 |
'i': i, |
|
|
133 |
'x': mymask |
|
|
134 |
} |
|
|
135 |
for aa_idx, aa in enumerate(a): |
|
|
136 |
myargs['a-MULTINAME-%i'%aa_idx] = aa |
|
|
137 |
|
|
|
138 |
processed_args = process_arguments(myargs) |
|
|
139 |
libfn = get_lib_fn('Atropos') |
|
|
140 |
retval = libfn(processed_args) |
|
|
141 |
|
|
|
142 |
if retval != 0: |
|
|
143 |
raise Exception(f"Atropos exited with non-zero status {retval}. Run with verbose=1 to see error messages") |
|
|
144 |
|
|
|
145 |
probsout = glob.glob(os.path.join(tdir,'*'+searchpattern)) |
|
|
146 |
|
|
|
147 |
if probsout is None or len(probsout) == 0: |
|
|
148 |
raise Exception('No Atropos output probability images found. Run with verbose=1 to see error messages') |
|
|
149 |
|
|
|
150 |
probsout.sort() |
|
|
151 |
probimgs = [ants.image_read(probsout[0])] |
|
|
152 |
for idx in range(1, len(probsout)): |
|
|
153 |
probimgs.append(ants.image_read(probsout[idx])) |
|
|
154 |
|
|
|
155 |
outimg = outimg.clone('float') |
|
|
156 |
return {'segmentation': outimg, |
|
|
157 |
'probabilityimages': probimgs} |