dbdicom 0.2.0__py3-none-any.whl → 0.2.3__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.
Potentially problematic release.
This version of dbdicom might be problematic. Click here for more details.
- dbdicom/__init__.py +5 -3
- dbdicom/create.py +77 -70
- dbdicom/dro.py +174 -0
- dbdicom/ds/dataset.py +30 -3
- dbdicom/ds/types/mr_image.py +18 -7
- dbdicom/extensions/__init__.py +10 -0
- dbdicom/{wrappers → extensions}/dipy.py +191 -205
- dbdicom/extensions/elastix.py +503 -0
- dbdicom/extensions/matplotlib.py +107 -0
- dbdicom/extensions/numpy.py +271 -0
- dbdicom/{wrappers → extensions}/scipy.py +131 -32
- dbdicom/{wrappers → extensions}/skimage.py +1 -1
- dbdicom/extensions/sklearn.py +243 -0
- dbdicom/extensions/vreg.py +1390 -0
- dbdicom/external/dcm4che/bin/emf2sf +57 -57
- dbdicom/manager.py +91 -36
- dbdicom/pipelines.py +66 -0
- dbdicom/record.py +447 -80
- dbdicom/types/instance.py +46 -20
- dbdicom/types/series.py +2182 -399
- dbdicom/utils/image.py +152 -21
- dbdicom/utils/variables.py +8 -2
- dbdicom/utils/vreg.py +327 -135
- dbdicom-0.2.3.dist-info/METADATA +88 -0
- dbdicom-0.2.3.dist-info/RECORD +67 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/WHEEL +1 -1
- dbdicom/external/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/lib/linux-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libopencv_java.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86-64/libclib_jiio.so +0 -0
- dbdicom/wrappers/__init__.py +0 -7
- dbdicom/wrappers/elastix.py +0 -855
- dbdicom/wrappers/numpy.py +0 -119
- dbdicom/wrappers/sklearn.py +0 -151
- dbdicom/wrappers/vreg.py +0 -273
- dbdicom-0.2.0.dist-info/METADATA +0 -276
- dbdicom-0.2.0.dist-info/RECORD +0 -81
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/LICENSE +0 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/top_level.txt +0 -0
dbdicom/wrappers/numpy.py
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Functions for a new (sub)package dbimage inside dbdicom
|
|
3
|
-
|
|
4
|
-
A set of dbdicom wrappers for numpy
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def mean_intensity_projection(series):
|
|
11
|
-
"""
|
|
12
|
-
Segment by thresholding
|
|
13
|
-
|
|
14
|
-
Parameters
|
|
15
|
-
----------
|
|
16
|
-
series: dbdicom series (4D: slice + time)
|
|
17
|
-
|
|
18
|
-
Returns
|
|
19
|
-
-------
|
|
20
|
-
mean : dbdicom series (3D)
|
|
21
|
-
|
|
22
|
-
Example:
|
|
23
|
-
|
|
24
|
-
mean_series = mean(series, axis=-1)
|
|
25
|
-
"""
|
|
26
|
-
# Get numpy array with dimensions (slice, time, x, y)
|
|
27
|
-
# array = series.array('SliceLocation')
|
|
28
|
-
|
|
29
|
-
# Get numpy array with dimensions (x, y, slice, time)
|
|
30
|
-
array, headers = series.array('SliceLocation', pixels_first=True)
|
|
31
|
-
array = np.mean(array, axis=-1)
|
|
32
|
-
desc = series.instance().SeriesDescription + ' [Mean Intensity Projection]'
|
|
33
|
-
new_series = series.new_sibling(SeriesDescription=desc)
|
|
34
|
-
new_series.set_array(array, headers[:,0], pixels_first=True)
|
|
35
|
-
return new_series
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def maximum_intensity_projection(series):
|
|
39
|
-
"""
|
|
40
|
-
Segment by thresholding
|
|
41
|
-
|
|
42
|
-
Parameters
|
|
43
|
-
----------
|
|
44
|
-
series: dbdicom series (4D: slice + time)
|
|
45
|
-
|
|
46
|
-
Returns
|
|
47
|
-
-------
|
|
48
|
-
mean : dbdicom series (3D)
|
|
49
|
-
|
|
50
|
-
Example:
|
|
51
|
-
|
|
52
|
-
mean_series = mean(series, axis=-1)
|
|
53
|
-
"""
|
|
54
|
-
# Get numpy array with dimensions (slice, time, x, y)
|
|
55
|
-
# array = series.array('SliceLocation')
|
|
56
|
-
|
|
57
|
-
# Get numpy array with dimensions (x, y, slice, time)
|
|
58
|
-
array, headers = series.array('SliceLocation', pixels_first=True)
|
|
59
|
-
array = np.amax(array, axis=-1)
|
|
60
|
-
desc = series.instance().SeriesDescription + ' [Maximum Intensity Projection]'
|
|
61
|
-
new_series = series.new_sibling(SeriesDescription=desc)
|
|
62
|
-
new_series.set_array(array, headers[:,0], pixels_first=True)
|
|
63
|
-
return new_series
|
|
64
|
-
|
|
65
|
-
def norm_projection(series, ord=None):
|
|
66
|
-
array, headers = series.array('SliceLocation', pixels_first=True)
|
|
67
|
-
array = np.linalg.norm(array, ord=ord, axis=-1)
|
|
68
|
-
desc = series.instance().SeriesDescription + ' [Norm projection]'
|
|
69
|
-
new_series = series.new_sibling(SeriesDescription=desc)
|
|
70
|
-
new_series.set_array(array, headers[:,0], pixels_first=True)
|
|
71
|
-
return new_series
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def threshold(input, low_threshold=0, high_threshold=1, method='absolute'):
|
|
76
|
-
"""
|
|
77
|
-
Segment by thresholding
|
|
78
|
-
|
|
79
|
-
Parameters
|
|
80
|
-
----------
|
|
81
|
-
input: dbdicom series
|
|
82
|
-
|
|
83
|
-
Returns
|
|
84
|
-
-------
|
|
85
|
-
filtered : dbdicom series
|
|
86
|
-
"""
|
|
87
|
-
suffix = ' [Threshold segmentation]'
|
|
88
|
-
desc = input.instance().SeriesDescription
|
|
89
|
-
filtered = input.copy(SeriesDescription = desc+suffix)
|
|
90
|
-
#images = filtered.instances()
|
|
91
|
-
images = filtered.images()
|
|
92
|
-
for i, image in enumerate(images):
|
|
93
|
-
input.status.progress(i+1, len(images), 'Filtering ' + desc)
|
|
94
|
-
image.read()
|
|
95
|
-
array = image.array()
|
|
96
|
-
if method == 'quantiles':
|
|
97
|
-
range = np.quantile(array, [low_threshold, high_threshold])
|
|
98
|
-
elif method == 'range':
|
|
99
|
-
min, max = np.amin(array), np.amax(array)
|
|
100
|
-
range = [min+low_threshold*(max-min), min+high_threshold*(max-min)]
|
|
101
|
-
else:
|
|
102
|
-
range = [low_threshold, high_threshold]
|
|
103
|
-
array = np.logical_and(array > range[0], array < range[1])
|
|
104
|
-
image.set_array(array)
|
|
105
|
-
array = array.astype(np.ubyte)
|
|
106
|
-
_reset_window(image, array)
|
|
107
|
-
image.clear()
|
|
108
|
-
input.status.hide()
|
|
109
|
-
return filtered
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# Helper functions
|
|
114
|
-
|
|
115
|
-
def _reset_window(image, array):
|
|
116
|
-
min = np.amin(array)
|
|
117
|
-
max = np.amax(array)
|
|
118
|
-
image.WindowCenter= (max+min)/2
|
|
119
|
-
image.WindowWidth = 0.9*(max-min)
|
dbdicom/wrappers/sklearn.py
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from sklearn.cluster import KMeans
|
|
3
|
-
import dbdicom.wrappers.scipy as scipy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans
|
|
7
|
-
def kmeans(features, mask=None, n_clusters=2, multiple_series=False):
|
|
8
|
-
"""
|
|
9
|
-
Labels structures in an image
|
|
10
|
-
|
|
11
|
-
Wrapper for sklearn.cluster.KMeans function.
|
|
12
|
-
|
|
13
|
-
Parameters
|
|
14
|
-
----------
|
|
15
|
-
input: list of dbdicom series (one for each feature)
|
|
16
|
-
mask: optional mask for clustering
|
|
17
|
-
|
|
18
|
-
Returns
|
|
19
|
-
-------
|
|
20
|
-
clusters : list of dbdicom series, with labels per cluster.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
# If a mask is provided, map it onto the reference feature and
|
|
24
|
-
# extract the indices of all pixels under the mask
|
|
25
|
-
if mask is not None:
|
|
26
|
-
mask.status.message('Reading mask array..')
|
|
27
|
-
mask = scipy.map_to(mask, features[0], mask=True)
|
|
28
|
-
mask_array, _ = mask.array(['SliceLocation', 'AcquisitionTime'], pixels_first=True)
|
|
29
|
-
mask_array = np.ravel(mask_array)
|
|
30
|
-
mask_indices = tuple(mask_array.nonzero())
|
|
31
|
-
|
|
32
|
-
# Ensure all the features are in the same geometry as the reference feature
|
|
33
|
-
features = scipy.overlay(features)
|
|
34
|
-
|
|
35
|
-
# Create array with shape (n_samples, n_features) and mask if needed.
|
|
36
|
-
array = []
|
|
37
|
-
for s, series in enumerate(features):
|
|
38
|
-
series.status.progress(s+1, len(features), 'Reading features..')
|
|
39
|
-
arr, headers = series.array(['SliceLocation', 'AcquisitionTime'], pixels_first=True)
|
|
40
|
-
shape = arr.shape
|
|
41
|
-
arr = np.ravel(arr)
|
|
42
|
-
if mask is not None:
|
|
43
|
-
arr = arr[mask_indices]
|
|
44
|
-
array.append(arr)
|
|
45
|
-
array = np.vstack(array).T
|
|
46
|
-
|
|
47
|
-
# Perform the K-Means clustering.
|
|
48
|
-
series.status.message('Clustering. Please be patient - this is hard work..')
|
|
49
|
-
kmeans = KMeans(n_clusters=n_clusters, random_state=0, n_init=3, verbose=1).fit(array)
|
|
50
|
-
|
|
51
|
-
# Create an output array for the labels
|
|
52
|
-
if mask is not None:
|
|
53
|
-
mask.status.message('Creating output array..')
|
|
54
|
-
array = np.zeros(shape)
|
|
55
|
-
array = np.ravel(array)
|
|
56
|
-
array[mask_indices] = 1+kmeans.labels_
|
|
57
|
-
else:
|
|
58
|
-
array = 1+kmeans.labels_
|
|
59
|
-
array = array.reshape(shape)
|
|
60
|
-
|
|
61
|
-
# Save the results
|
|
62
|
-
series.status.message('Saving clusters..')
|
|
63
|
-
if multiple_series:
|
|
64
|
-
# Save each cluster as a separate mask
|
|
65
|
-
clusters = []
|
|
66
|
-
for cluster in range(1,1+n_clusters):
|
|
67
|
-
array_cluster = np.zeros(array.shape)
|
|
68
|
-
array_cluster[array == cluster] = 1
|
|
69
|
-
series_cluster = features[0].new_sibling(SeriesDescription = 'KMeans cluster ' + str(cluster))
|
|
70
|
-
series_cluster.set_array(array_cluster, headers, pixels_first=True)
|
|
71
|
-
_reset_window(series_cluster, array_cluster)
|
|
72
|
-
clusters.append(series_cluster)
|
|
73
|
-
else:
|
|
74
|
-
# Save the label array in a single series
|
|
75
|
-
clusters = features[0].new_sibling(SeriesDescription = 'KMeans')
|
|
76
|
-
clusters.set_array(array, headers, pixels_first=True)
|
|
77
|
-
_reset_window(clusters, array)
|
|
78
|
-
|
|
79
|
-
return clusters
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def sequential_kmeans(features, mask=None, n_clusters=2, multiple_series=False):
|
|
84
|
-
"""
|
|
85
|
-
Labels structures in an image using sequential k-means clustering
|
|
86
|
-
|
|
87
|
-
Sequential here means that the clustering is always performed on a single feature
|
|
88
|
-
using the output of the previous iteration as a mask for the next.
|
|
89
|
-
|
|
90
|
-
Parameters
|
|
91
|
-
----------
|
|
92
|
-
input: list of dbdicom series (one for each feature)
|
|
93
|
-
mask: optional mask for clustering
|
|
94
|
-
|
|
95
|
-
Returns
|
|
96
|
-
-------
|
|
97
|
-
clusters : list of dbdicom series, with labels per cluster.
|
|
98
|
-
"""
|
|
99
|
-
|
|
100
|
-
f = features[0]
|
|
101
|
-
clusters = kmeans([f], mask=mask, n_clusters=n_clusters, multiple_series=True)
|
|
102
|
-
for f in features[1:]:
|
|
103
|
-
cf = []
|
|
104
|
-
for mask in clusters:
|
|
105
|
-
cf += kmeans([f], mask=mask, n_clusters=n_clusters, multiple_series=True)
|
|
106
|
-
mask.remove()
|
|
107
|
-
clusters = cf
|
|
108
|
-
|
|
109
|
-
# Return clusters as a list
|
|
110
|
-
if multiple_series:
|
|
111
|
-
return clusters
|
|
112
|
-
|
|
113
|
-
# Return a single label series
|
|
114
|
-
label = masks_to_label(clusters)
|
|
115
|
-
for c in clusters:
|
|
116
|
-
c.remove()
|
|
117
|
-
return label
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def masks_to_label(masks):
|
|
122
|
-
"Convert a list of masks into a single label series"
|
|
123
|
-
|
|
124
|
-
# Ensure all the masks are in the same geometry
|
|
125
|
-
masks = scipy.overlay(masks)
|
|
126
|
-
|
|
127
|
-
# Create a single label array
|
|
128
|
-
array, headers = masks[0].array(['SliceLocation', 'AcquisitionTime'], pixels_first=True)
|
|
129
|
-
msg = 'Creating a single label image'
|
|
130
|
-
for m, mask in enumerate(masks[1:]):
|
|
131
|
-
mask.status.progress(m+1, len(masks)-1, msg)
|
|
132
|
-
arr, _ = mask.array(['SliceLocation', 'AcquisitionTime'], pixels_first=True)
|
|
133
|
-
ind = tuple(arr.nonzero())
|
|
134
|
-
array[ind] = m+2
|
|
135
|
-
|
|
136
|
-
# Save label array to disk
|
|
137
|
-
desc = masks[0].SeriesDescription
|
|
138
|
-
label = masks[0].new_sibling(SeriesDescription = desc + ' [Label]')
|
|
139
|
-
label.set_array(array, headers, pixels_first=True)
|
|
140
|
-
|
|
141
|
-
return label
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# Helper functions
|
|
146
|
-
|
|
147
|
-
def _reset_window(image, array):
|
|
148
|
-
min = np.amin(array)
|
|
149
|
-
max = np.amax(array)
|
|
150
|
-
image.WindowCenter= (max+min)/2
|
|
151
|
-
image.WindowWidth = 0.9*(max-min)
|
dbdicom/wrappers/vreg.py
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
from dbdicom.utils import vreg
|
|
3
|
-
|
|
4
|
-
def print_current(vk):
|
|
5
|
-
print(vk)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _get_input(moving, static):
|
|
9
|
-
|
|
10
|
-
# Get affine matrices and check that there is a single value
|
|
11
|
-
affine_moving = moving.affine_matrix()
|
|
12
|
-
if isinstance(affine_moving, list):
|
|
13
|
-
msg = 'This function only works for series with a single slice group. \n'
|
|
14
|
-
msg += 'Multiple slice groups detected in moving series - please split the series first.'
|
|
15
|
-
raise ValueError(msg)
|
|
16
|
-
else:
|
|
17
|
-
affine_moving = affine_moving[0]
|
|
18
|
-
affine_static = static.affine_matrix()
|
|
19
|
-
if isinstance(affine_static, list):
|
|
20
|
-
msg = 'This function only works for series with a single slice group. \n'
|
|
21
|
-
msg += 'Multiple slice groups detected in static series - please split the series first.'
|
|
22
|
-
raise ValueError(msg)
|
|
23
|
-
else:
|
|
24
|
-
affine_static = affine_static[0]
|
|
25
|
-
|
|
26
|
-
# Get arrays for static and moving series
|
|
27
|
-
array_static, headers_static = static.array('SliceLocation', pixels_first=True, first_volume=True)
|
|
28
|
-
if array_static is None:
|
|
29
|
-
msg = 'Fixed series is empty - cannot perform alignment.'
|
|
30
|
-
raise ValueError(msg)
|
|
31
|
-
array_moving, headers_moving = moving.array('SliceLocation', pixels_first=True, first_volume=True)
|
|
32
|
-
if array_moving is None:
|
|
33
|
-
msg = 'Moving series is empty - cannot perform alignment.'
|
|
34
|
-
raise ValueError(msg)
|
|
35
|
-
|
|
36
|
-
moving.message('Performing coregistration. Please be patient. Its hard work and I need to concentrate..')
|
|
37
|
-
|
|
38
|
-
return array_static, affine_static, array_moving, affine_moving, headers_static, headers_moving
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def translation_sos(moving, static, tolerance=0.1):
|
|
45
|
-
|
|
46
|
-
array_static, affine_static, array_moving, affine_moving, headers, _ = _get_input(moving, static)
|
|
47
|
-
|
|
48
|
-
# Define initial values and optimization
|
|
49
|
-
_, _, static_pixel_spacing = vreg.affine_components(affine_static)
|
|
50
|
-
optimization = {
|
|
51
|
-
'method': 'GD',
|
|
52
|
-
'options': {'gradient step': static_pixel_spacing, 'tolerance': tolerance},
|
|
53
|
-
'callback': lambda vk: moving.status.message('Current parameter: ' + str(vk)),
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
# Align volumes
|
|
57
|
-
try:
|
|
58
|
-
translation_estimate = vreg.align(
|
|
59
|
-
moving = array_moving,
|
|
60
|
-
moving_affine = affine_moving,
|
|
61
|
-
static = array_static,
|
|
62
|
-
static_affine = affine_static,
|
|
63
|
-
parameters = np.array([0, 0, 0], dtype=np.float32),
|
|
64
|
-
resolutions = [4,2,1],
|
|
65
|
-
transformation = vreg.translate,
|
|
66
|
-
metric = vreg.sum_of_squares,
|
|
67
|
-
optimization = optimization,
|
|
68
|
-
)
|
|
69
|
-
except:
|
|
70
|
-
print('Failed to align volumes..')
|
|
71
|
-
return None
|
|
72
|
-
|
|
73
|
-
coregistered = vreg.translate(array_moving, affine_moving, array_static.shape, affine_static, translation_estimate)
|
|
74
|
-
|
|
75
|
-
# Save results as new dicom series
|
|
76
|
-
moving.message('Writing coregistered series to database..')
|
|
77
|
-
desc = moving.instance().SeriesDescription
|
|
78
|
-
coreg = moving.new_sibling(SeriesDescription = desc + ' [translation]')
|
|
79
|
-
coreg.set_array(coregistered, headers, pixels_first=True)
|
|
80
|
-
return coreg
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def rigid_sos(moving, static, tolerance=0.1):
|
|
84
|
-
|
|
85
|
-
array_static, affine_static, array_moving, affine_moving, headers, _ = _get_input(moving, static)
|
|
86
|
-
|
|
87
|
-
# Define initial values and optimization
|
|
88
|
-
_, _, static_pixel_spacing = vreg.affine_components(affine_static)
|
|
89
|
-
rot_gradient_step, translation_gradient_step, _ = vreg.affine_resolution(array_static.shape, static_pixel_spacing)
|
|
90
|
-
gradient_step = np.concatenate((1.0*rot_gradient_step, 0.5*translation_gradient_step))
|
|
91
|
-
optimization = {
|
|
92
|
-
'method': 'GD',
|
|
93
|
-
'options': {'gradient step': gradient_step, 'tolerance': tolerance},
|
|
94
|
-
'callback': lambda vk: moving.message('Current parameter: ' + str(vk)),
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
# Align volumes
|
|
98
|
-
try:
|
|
99
|
-
rigid_estimate = vreg.align(
|
|
100
|
-
moving = array_moving,
|
|
101
|
-
moving_affine = affine_moving,
|
|
102
|
-
static = array_static,
|
|
103
|
-
static_affine = affine_static,
|
|
104
|
-
parameters = np.array([0, 0, 0, 0, 0, 0], dtype=np.float32),
|
|
105
|
-
resolutions = [4,2,1],
|
|
106
|
-
transformation = vreg.rigid,
|
|
107
|
-
metric = vreg.sum_of_squares,
|
|
108
|
-
optimization = optimization,
|
|
109
|
-
)
|
|
110
|
-
except:
|
|
111
|
-
print('Failed to align volumes..')
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
coregistered = vreg.rigid(array_moving, affine_moving, array_static.shape, affine_static, rigid_estimate)
|
|
115
|
-
|
|
116
|
-
moving.message('Writing coregistered series to database..')
|
|
117
|
-
|
|
118
|
-
# Save results as new dicom series
|
|
119
|
-
desc = moving.instance().SeriesDescription
|
|
120
|
-
coreg = moving.new_sibling(SeriesDescription = desc + ' [rigid]')
|
|
121
|
-
coreg.set_array(coregistered, headers, pixels_first=True)
|
|
122
|
-
return coreg
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def sbs_translation_sos(moving, static, tolerance=0.1):
|
|
127
|
-
|
|
128
|
-
array_static, affine_static, array_moving, affine_moving, headers, headers_moving = _get_input(moving, static)
|
|
129
|
-
slice_thickness = [headers_moving[z].SliceThickness for z in range(headers_moving.size)]
|
|
130
|
-
|
|
131
|
-
# Define initial values and optimization
|
|
132
|
-
_, _, static_pixel_spacing = vreg.affine_components(affine_static)
|
|
133
|
-
optimization = {
|
|
134
|
-
'method': 'GD',
|
|
135
|
-
'options': {'gradient step': static_pixel_spacing, 'tolerance': tolerance},
|
|
136
|
-
# 'callback': lambda vk: moving.message('Current parameter: ' + str(vk)),
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
# Perform coregistration
|
|
140
|
-
estimate = vreg.align_slice_by_slice(
|
|
141
|
-
moving = array_moving,
|
|
142
|
-
static = array_static,
|
|
143
|
-
parameters = np.array([0, 0, 0], dtype=np.float32),
|
|
144
|
-
moving_affine = affine_moving,
|
|
145
|
-
static_affine = affine_static,
|
|
146
|
-
transformation = vreg.translate,
|
|
147
|
-
metric = vreg.sum_of_squares,
|
|
148
|
-
optimization = optimization,
|
|
149
|
-
resolutions = [4,2,1],
|
|
150
|
-
slice_thickness = slice_thickness,
|
|
151
|
-
progress = lambda z, nz: moving.progress(z, nz, 'Coregistering slice-by-slice using translations'),
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# Calculate coregistered
|
|
155
|
-
coregistered = vreg.transform_slice_by_slice(array_moving, affine_moving, array_static.shape, affine_static, estimate, vreg.translate, slice_thickness)
|
|
156
|
-
# coregistered = vreg.transform_slice_by_slice(
|
|
157
|
-
# moving = array_moving,
|
|
158
|
-
# moving_affine = affine_moving,
|
|
159
|
-
# static_shape = array_static.shape,
|
|
160
|
-
# static_affine = affine_static,
|
|
161
|
-
# parameters = estimate,
|
|
162
|
-
# transformation = vreg.translate,
|
|
163
|
-
# slice_thickness = slice_thickness)
|
|
164
|
-
|
|
165
|
-
# Save results as new dicom series
|
|
166
|
-
moving.message('Writing coregistered series to database..')
|
|
167
|
-
desc = moving.instance().SeriesDescription
|
|
168
|
-
coreg = moving.new_sibling(SeriesDescription = desc + ' [sbs translation]')
|
|
169
|
-
coreg.set_array(coregistered, headers, pixels_first=True)
|
|
170
|
-
|
|
171
|
-
return coreg
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def sbs_rigid_around_com_sos(moving, static, tolerance=0.1):
|
|
175
|
-
|
|
176
|
-
array_static, affine_static, array_moving, affine_moving, headers, headers_moving = _get_input(moving, static)
|
|
177
|
-
slice_thickness = [headers_moving[z].SliceThickness for z in range(headers_moving.size)]
|
|
178
|
-
|
|
179
|
-
# Define initial values and optimization
|
|
180
|
-
_, _, static_pixel_spacing = vreg.affine_components(affine_static)
|
|
181
|
-
rot_gradient_step, translation_gradient_step, _ = vreg.affine_resolution(array_static.shape, static_pixel_spacing)
|
|
182
|
-
gradient_step = np.concatenate((1.0*rot_gradient_step, 0.5*translation_gradient_step))
|
|
183
|
-
optimization = {
|
|
184
|
-
'method': 'GD',
|
|
185
|
-
'options': {'gradient step': gradient_step, 'tolerance': tolerance},
|
|
186
|
-
#'callback': lambda vk: moving.message('Current parameter: ' + str(vk)),
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
# Perform coregistration
|
|
190
|
-
estimate = vreg.align_slice_by_slice(
|
|
191
|
-
moving = array_moving,
|
|
192
|
-
static = array_static,
|
|
193
|
-
parameters = np.array([0, 0, 0, 0, 0, 0], dtype=np.float32),
|
|
194
|
-
moving_affine = affine_moving,
|
|
195
|
-
static_affine = affine_static,
|
|
196
|
-
transformation = vreg.rigid_around_com,
|
|
197
|
-
metric = vreg.sum_of_squares,
|
|
198
|
-
optimization = optimization,
|
|
199
|
-
resolutions = [4,2,1],
|
|
200
|
-
slice_thickness = slice_thickness,
|
|
201
|
-
progress = lambda z, nz: moving.progress(z, nz, 'Coregistering slice-by-slice using rigid transformations'),
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
# The generic slice-by-slice transform does not work for center of mass rotations.
|
|
205
|
-
# Calculate rotation center and use rigid rotation around given center instead.
|
|
206
|
-
estimate_center = []
|
|
207
|
-
for z in range(len(estimate)):
|
|
208
|
-
array_moving_z, affine_moving_z = vreg.extract_slice(array_moving, affine_moving, z, slice_thickness)
|
|
209
|
-
center = estimate[z][3:] + vreg.center_of_mass(vreg.to_3d(array_moving_z), affine_moving_z)
|
|
210
|
-
pars = np.concatenate((estimate[z][:3], center, estimate[z][3:]))
|
|
211
|
-
estimate_center.append(pars)
|
|
212
|
-
|
|
213
|
-
# Calculate coregistered (using rigid around known center)
|
|
214
|
-
coregistered = vreg.transform_slice_by_slice(array_moving, affine_moving, array_static.shape, affine_static, estimate_center, vreg.rigid_around, slice_thickness)
|
|
215
|
-
# coregistered = vreg.transform_slice_by_slice(
|
|
216
|
-
# moving = array_moving,
|
|
217
|
-
# moving_affine = affine_moving,
|
|
218
|
-
# static_shape = array_static.shape,
|
|
219
|
-
# static_affine = affine_static,
|
|
220
|
-
# parameters = estimate_center,
|
|
221
|
-
# transformation = vreg.rigid_around,
|
|
222
|
-
# slice_thickness = slice_thickness)
|
|
223
|
-
|
|
224
|
-
# Save results as new dicom series
|
|
225
|
-
moving.message('Writing coregistered series to database..')
|
|
226
|
-
desc = moving.instance().SeriesDescription
|
|
227
|
-
coreg = moving.new_sibling(SeriesDescription = desc + ' [sbs rigid com]')
|
|
228
|
-
coreg.set_array(coregistered, headers, pixels_first=True)
|
|
229
|
-
|
|
230
|
-
return coreg
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def rigid_around_com_sos(moving, static, tolerance=0.1):
|
|
235
|
-
|
|
236
|
-
array_static, affine_static, array_moving, affine_moving, headers, _ = _get_input(moving, static)
|
|
237
|
-
|
|
238
|
-
# Define initial values and optimization
|
|
239
|
-
_, _, static_pixel_spacing = vreg.affine_components(affine_static)
|
|
240
|
-
rot_gradient_step, translation_gradient_step, _ = vreg.affine_resolution(array_static.shape, static_pixel_spacing)
|
|
241
|
-
gradient_step = np.concatenate((1.0*rot_gradient_step, 0.5*translation_gradient_step))
|
|
242
|
-
optimization = {
|
|
243
|
-
'method': 'GD',
|
|
244
|
-
'options': {'gradient step': gradient_step, 'tolerance': tolerance},
|
|
245
|
-
'callback': lambda vk: moving.message('Current parameter: ' + str(vk)),
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
# Align volumes
|
|
249
|
-
try:
|
|
250
|
-
rigid_estimate = vreg.align(
|
|
251
|
-
moving = array_moving,
|
|
252
|
-
moving_affine = affine_moving,
|
|
253
|
-
static = array_static,
|
|
254
|
-
static_affine = affine_static,
|
|
255
|
-
parameters = np.array([0, 0, 0, 0, 0, 0], dtype=np.float32),
|
|
256
|
-
resolutions = [4,2,1],
|
|
257
|
-
transformation = vreg.rigid_around_com,
|
|
258
|
-
metric = vreg.sum_of_squares,
|
|
259
|
-
optimization = optimization,
|
|
260
|
-
)
|
|
261
|
-
except:
|
|
262
|
-
print('Failed to align volumes..')
|
|
263
|
-
return None
|
|
264
|
-
|
|
265
|
-
coregistered = vreg.rigid_around_com(array_moving, affine_moving, array_static.shape, affine_static, rigid_estimate)
|
|
266
|
-
|
|
267
|
-
moving.message('Writing coregistered series to database..')
|
|
268
|
-
|
|
269
|
-
# Save results as new dicom series
|
|
270
|
-
desc = moving.instance().SeriesDescription
|
|
271
|
-
coreg = moving.new_sibling(SeriesDescription = desc + ' [rigid com]')
|
|
272
|
-
coreg.set_array(coregistered, headers, pixels_first=True)
|
|
273
|
-
return coreg
|