@@ -42,7 +42,6 @@ def _read_projections(folder, indices):
4242 """Read mayo projections from a folder."""
4343 datasets = []
4444 data_array = []
45- angles = []
4645
4746 # Get the relevant file names
4847 file_names = sorted ([f for f in os .listdir (folder ) if f .endswith (".dcm" )])
@@ -82,19 +81,15 @@ def _read_projections(folder, indices):
8281 proj_array = np .array (np .frombuffer (dataset .PixelData , 'H' ),
8382 dtype = 'float32' )
8483 proj_array = proj_array .reshape ([cols , rows ])
85- # proj_array = proj_array.reshape([rows, cols], order='F').T
86-
87- # Rescale array (no HU)
88- proj_array *= rescale_slope
89- proj_array += rescale_intercept
90-
9184 data_array .append (proj_array [:, ::- 1 ])
92- angles .append (dataset .DetectorFocalCenterAngularPosition )
9385 datasets .append (dataset )
9486
9587 data_array = np .array (data_array )
96- angles = np .array (angles )
97- return datasets , data_array , angles
88+ # Rescale array
89+ data_array *= rescale_slope
90+ data_array += rescale_intercept
91+
92+ return datasets , data_array
9893
9994
10095def load_projections (folder , indices = None , use_ffs = True ):
@@ -107,10 +102,9 @@ def load_projections(folder, indices=None, use_ffs=True):
107102 indices : optional
108103 Indices of the projections to load.
109104 Accepts advanced indexing such as slice or list of indices.
110- num_slices: int, optional
111- Number of slices to consider for the reconstruction volume;
112- the other parameters are hard-coded. With default value (None)
113- a *temporary* volume is created.
105+ use_ffs : bool, optional
106+ If ``True``, a source shift is applied to compensate the flying focal spot.
107+ Default: ``True``
114108
115109 Returns
116110 -------
@@ -120,10 +114,12 @@ def load_projections(folder, indices=None, use_ffs=True):
120114 Projection data, given as the line integral of the linear attenuation
121115 coefficient (g/cm^3). Its unit is thus g/cm^2.
122116 """
123- datasets , data_array , angles = _read_projections (folder , indices )
117+ datasets , data_array = _read_projections (folder , indices )
124118
119+ # Get the angles
120+ angles = np .array ([d .DetectorFocalCenterAngularPosition for d in datasets ])
125121 # Reverse angular axis and set origin at 6 o'clock
126- angles = - np .unwrap (angles ) - np .pi
122+ angles = - np .unwrap (angles ) - np .pi
127123
128124 # Set minimum and maximum corners
129125 det_shape = np .array ([datasets [0 ].NumberofDetectorColumns ,
@@ -148,11 +144,11 @@ def load_projections(folder, indices=None, use_ffs=True):
148144 num_rot = (angles [- 1 ] - angles [0 ]) / (2 * np .pi )
149145 pitch = table_dist / num_rot
150146
151- # offsets: focal spot -> detector’s focal center
147+ # offsets: detector’s focal center -> focal spot
152148 offset_angular = np .array ([d .SourceAngularPositionShift for d in datasets ])
153149 offset_radial = np .array ([d .SourceRadialDistanceShift for d in datasets ])
154150 offset_axial = np .array ([d .SourceAxialPositionShift for d in datasets ])
155-
151+
156152 # angles have inverse convention
157153 shift_d = np .cos (- offset_angular ) * (src_radius + offset_radial ) - src_radius
158154 shift_t = np .sin (- offset_angular ) * (src_radius + offset_radial )
@@ -164,7 +160,6 @@ def load_projections(folder, indices=None, use_ffs=True):
164160 detector_partition = odl .uniform_partition (det_minp , det_maxp , det_shape )
165161
166162 # Convert offset to odl definitions
167- # offset_along_axis = (mean_offset_along_axis_for_ffz +
168163 offset_along_axis = datasets [0 ].DetectorFocalCenterAxialPosition - \
169164 angles [0 ] / (2 * np .pi ) * pitch
170165
@@ -187,35 +182,26 @@ def load_projections(folder, indices=None, use_ffs=True):
187182
188183 return geometry , data_array
189184
190-
191185
192- def get_default_recon_space ():
193- # Create a *temporary* ray transform (we need its range)
194- num_slices = 97
195- pixel_spacing = np .array ([0.75 ,0.75 ])
196- num_pixel = np .array ([512 ,512 ])
197- slice_dist = 5.
198- origin = np .zeros (3 )
199- mid_table = (datasets [0 ].DetectorFocalCenterAxialPosition +
200- datasets [- 1 ].DetectorFocalCenterAxialPosition ) / 2
201- min_pt = np .copy (origin )
202- min_pt [:2 ] -= pixel_spacing * num_pixel / 2
203- min_pt [2 ] += mid_table - num_slices * slice_dist / 2
204-
205- max_pt = np .copy (min_pt )
206- max_pt [:2 ] += pixel_spacing * num_pixel
207- max_pt [2 ] += num_slices * slice_dist
208-
209- recon_dim = np .array ([* num_pixel , num_slices ], dtype = np .int32 )
210- recon_space = odl .uniform_discr (min_pt , max_pt ,
211- shape = recon_dim ,
212- dtype = np .float32 )
213- return recon_space
186+ def interpolate_flat_grid (data_array , range_grid , radial_dist ):
187+ """Return the linear interpolator of the projection data on a flat detector.
214188
189+ Parameters
190+ ----------
191+ data_array : `numpy.ndarray`
192+ Projection data on the cylindrical detector that should be interpolated.
193+ range_grid : RectGrid
194+ Rectilinear grid on the flat detector.
195+ radial_dist : float
196+ The constant radial distance, that is the distance between the detector’s
197+ focal center and its central element.
215198
216- # ray_trafo = odl.tomo.RayTransform(recon_space, geometry, interp='linear')
199+ Returns
200+ -------
201+ proj_data : `numpy.ndarray`
202+ Interpolated projection data on the flat rectilinear grid.
203+ """
217204
218- def interpolate_flat_grid (data_array , range_grid , radial_dist ):
219205 # convert coordinates
220206 theta , up , vp = range_grid .meshgrid #ray_trafo.range.grid.meshgrid
221207 # d = src_radius + det_radius
@@ -225,14 +211,13 @@ def interpolate_flat_grid(data_array, range_grid, radial_dist):
225211 # Calculate projection data in rectangular coordinates since we have no
226212 # backend that supports cylindrical
227213 interpolator = linear_interpolator (
228- data_array , range_grid .coord_vectors # ray_trafo.range.grid.coord_vectors
214+ data_array , range_grid .coord_vectors
229215 )
230216 proj_data = interpolator ((theta , u , v ))
231217
232218 return proj_data
233219
234220
235-
236221def load_reconstruction (folder , slice_start = 0 , slice_end = - 1 ):
237222 """Load a volume from folder, also returns the corresponding partition.
238223
@@ -291,7 +276,6 @@ def load_reconstruction(folder, slice_start=0, slice_end=-1):
291276 data_array = np .rot90 (data_array , - 1 )
292277
293278 # Convert from storage type to densities
294- # TODO: Optimize these computations
295279 hu_values = (dataset .RescaleSlope * data_array +
296280 dataset .RescaleIntercept )
297281
0 commit comments