forked from mikgroup/sigpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sim.py
133 lines (98 loc) · 3.44 KB
/
sim.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# -*- coding: utf-8 -*-
"""Functions for simulations.
"""
import numpy as np
__all__ = ['shepp_logan']
def shepp_logan(shape, dtype=np.complex):
"""Generates a Shepp Logan phantom with a given shape and dtype.
Args:
shape (tuple of ints): shape, can be of length 2 or 3.
dtype (Dtype): data type.
Returns:
array.
"""
return phantom(shape, sl_amps, sl_scales, sl_offsets, sl_angles, dtype)
sl_amps = [1, -0.8, -0.2, -0.2, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
sl_scales = [[.6900, .920, .810], # white big
[.6624, .874, .780], # gray big
[.1100, .310, .220], # right black
[.1600, .410, .280], # left black
[.2100, .250, .410], # gray center blob
[.0460, .046, .050],
[.0460, .046, .050],
[.0460, .046, .050], # left small dot
[.0230, .023, .020], # mid small dot
[.0230, .023, .020]]
sl_offsets = [[0., 0., 0],
[0., -.0184, 0],
[.22, 0., 0],
[-.22, 0., 0],
[0., .35, -.15],
[0., .1, .25],
[0., -.1, .25],
[-.08, -.605, 0],
[0., -.606, 0],
[.06, -.605, 0]]
sl_angles = [[0, 0, 0],
[0, 0, 0],
[-18, 0, 10],
[18, 0, 10],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
def phantom(shape, amps, scales, offsets, angles, dtype):
"""
Generate a cube of given shape using a list of ellipsoid
parameters.
"""
if len(shape) == 2:
ndim = 2
shape = (1, shape[-2], shape[-1])
elif len(shape) == 3:
ndim = 3
else:
raise ValueError('Incorrect dimension')
out = np.zeros(shape, dtype=dtype)
z, y, x = np.mgrid[-(shape[-3] // 2):((shape[-3] + 1) // 2),
-(shape[-2] // 2):((shape[-2] + 1) // 2),
-(shape[-1] // 2):((shape[-1] + 1) // 2)]
coords = np.stack((x.ravel() / shape[-1] * 2,
y.ravel() / shape[-2] * 2,
z.ravel() / shape[-3] * 2))
for amp, scale, offset, angle in zip(amps, scales, offsets, angles):
ellipsoid(amp, scale, offset, angle, coords, out)
if ndim == 2:
return out[0, :, :]
else:
return out
def ellipsoid(amp, scale, offset, angle, coords, out):
"""
Generate a cube containing an ellipsoid defined by its parameters.
If out is given, fills the given cube instead of creating a new
one.
"""
R = rotation_matrix(angle)
coords = (np.matmul(R, coords) - np.reshape(offset, (3, 1))) / \
np.reshape(scale, (3, 1))
r2 = np.sum(coords ** 2, axis=0).reshape(out.shape)
out[r2 <= 1] += amp
def rotation_matrix(angle):
cphi = np.cos(np.radians(angle[0]))
sphi = np.sin(np.radians(angle[0]))
ctheta = np.cos(np.radians(angle[1]))
stheta = np.sin(np.radians(angle[1]))
cpsi = np.cos(np.radians(angle[2]))
spsi = np.sin(np.radians(angle[2]))
alpha = [[cpsi * cphi - ctheta * sphi * spsi,
cpsi * sphi + ctheta * cphi * spsi,
spsi * stheta],
[-spsi * cphi - ctheta * sphi * cpsi,
-spsi * sphi + ctheta * cphi * cpsi,
cpsi * stheta],
[stheta * sphi,
-stheta * cphi,
ctheta]]
return np.array(alpha)