3
3
import vtk , qt , ctk , slicer , vtkITK
4
4
from DICOMLib import DICOMPlugin
5
5
from DICOMLib import DICOMLoadable
6
+ from DICOMLib import DICOMUtils
6
7
from DICOMLib import DICOMExportScalarVolume
7
8
import logging
8
9
@@ -160,11 +161,6 @@ def examineFiles(self,files):
160
161
loadable .selected = True
161
162
# add it to the list of loadables later, if pixel data is available in at least one file
162
163
163
- # while looping through files, keep track of their
164
- # position and orientation for later use
165
- positions = {}
166
- orientations = {}
167
-
168
164
# make subseries volumes based on tag differences
169
165
subseriesTags = [
170
166
"seriesInstanceUID" ,
@@ -184,15 +180,6 @@ def examineFiles(self,files):
184
180
subseriesFiles = {}
185
181
subseriesValues = {}
186
182
for file in loadable .files :
187
-
188
- # save position and orientation
189
- positions [file ] = slicer .dicomDatabase .fileValue (file ,self .tags ['position' ])
190
- if positions [file ] == "" :
191
- positions [file ] = None
192
- orientations [file ] = slicer .dicomDatabase .fileValue (file ,self .tags ['orientation' ])
193
- if orientations [file ] == "" :
194
- orientations [file ] = None
195
-
196
183
# check for subseries values
197
184
for tag in subseriesTags :
198
185
value = slicer .dicomDatabase .fileValue (file ,self .tags [tag ])
@@ -250,100 +237,8 @@ def examineFiles(self,files):
250
237
# now for each series and subseries, sort the images
251
238
# by position and check for consistency
252
239
#
253
-
254
- # TODO: more consistency checks:
255
- # - is there gantry tilt?
256
- # - are the orientations the same for all slices?
257
240
for loadable in loadables :
258
- #
259
- # use the first file to get the ImageOrientationPatient for the
260
- # series and calculate the scan direction (assumed to be perpendicular
261
- # to the acquisition plane)
262
- #
263
- value = slicer .dicomDatabase .fileValue (loadable .files [0 ], self .tags ['numberOfFrames' ])
264
- if value != "" :
265
- loadable .warning += "Multi-frame image. If slice orientation or spacing is non-uniform then the image may be displayed incorrectly. Use with caution. "
266
-
267
- validGeometry = True
268
- ref = {}
269
- for tag in [self .tags ['position' ], self .tags ['orientation' ]]:
270
- value = slicer .dicomDatabase .fileValue (loadable .files [0 ], tag )
271
- if not value or value == "" :
272
- loadable .warning += "Reference image in series does not contain geometry information. Please use caution. "
273
- validGeometry = False
274
- loadable .confidence = 0.2
275
- break
276
- ref [tag ] = value
277
-
278
- if not validGeometry :
279
- continue
280
-
281
- # get the geometry of the scan
282
- # with respect to an arbitrary slice
283
- sliceAxes = [float (zz ) for zz in ref [self .tags ['orientation' ]].split ('\\ ' )]
284
- x = sliceAxes [:3 ]
285
- y = sliceAxes [3 :]
286
- scanAxis = self .cross (x ,y )
287
- scanOrigin = [float (zz ) for zz in ref [self .tags ['position' ]].split ('\\ ' )]
288
-
289
- acquisitionGeometryRegularizationEnabled = self .acquisitionGeometryRegularizationEnabled ()
290
-
291
- #
292
- # for each file in series, calculate the distance along
293
- # the scan axis, sort files by this
294
- #
295
- sortList = []
296
- missingGeometry = False
297
- for file in loadable .files :
298
- if not positions [file ]:
299
- missingGeometry = True
300
- break
301
- position = [float (zz ) for zz in positions [file ].split ('\\ ' )]
302
- vec = self .difference (position , scanOrigin )
303
- dist = self .dot (vec , scanAxis )
304
- sortList .append ((file , dist ))
305
-
306
- if missingGeometry :
307
- loadable .warning += "One or more images is missing geometry information. "
308
- else :
309
- sortedFiles = sorted (sortList , key = lambda x : x [1 ])
310
- distances = {}
311
- loadable .files = []
312
- for file ,dist in sortedFiles :
313
- loadable .files .append (file )
314
- distances [file ] = dist
315
-
316
- #
317
- # confirm equal spacing between slices
318
- # - use variable 'epsilon' to determine the tolerance
319
- #
320
- spaceWarnings = 0
321
- if len (loadable .files ) > 1 :
322
- file0 = loadable .files [0 ]
323
- file1 = loadable .files [1 ]
324
- dist0 = distances [file0 ]
325
- dist1 = distances [file1 ]
326
- spacing0 = dist1 - dist0
327
- n = 1
328
- for fileN in loadable .files [1 :]:
329
- fileNminus1 = loadable .files [n - 1 ]
330
- distN = distances [fileN ]
331
- distNminus1 = distances [fileNminus1 ]
332
- spacingN = distN - distNminus1
333
- spaceError = spacingN - spacing0
334
- if abs (spaceError ) > self .epsilon :
335
- spaceWarnings += 1
336
- loadable .warning += "Images are not equally spaced (a difference of %g vs %g in spacings was detected)." % (spaceError , spacing0 )
337
- if acquisitionGeometryRegularizationEnabled :
338
- loadable .warning += " Slicer apply a transform to this series trying to regularize the volume. Please use caution. "
339
- else :
340
- loadable .warning += (" If loaded image appears distorted, enable 'Acquisition geometry regularization'"
341
- " in Application settins / DICOM / DICOMScalarVolumePlugin. Please use caution. " )
342
- break
343
- n += 1
344
-
345
- if spaceWarnings != 0 :
346
- logging .warning ("Geometric issues were found with %d of the series. Please use caution." % spaceWarnings )
241
+ loadable .files , distances , loadable .warning = DICOMUtils .getSortedImageFiles (loadable .files , self .epsilon )
347
242
348
243
return loadables
349
244
@@ -363,22 +258,6 @@ def seriesSorter(self,x,y):
363
258
cmp = xNumber - yNumber
364
259
return cmp
365
260
366
- #
367
- # math utilities for processing dicom volumes
368
- # TODO: there must be good replacements for these
369
- #
370
- def cross (self , x , y ):
371
- return [x [1 ] * y [2 ] - x [2 ] * y [1 ],
372
- x [2 ] * y [0 ] - x [0 ] * y [2 ],
373
- x [0 ] * y [1 ] - x [1 ] * y [0 ]]
374
-
375
- def difference (self , x , y ):
376
- return [x [0 ] - y [0 ], x [1 ] - y [1 ], x [2 ] - y [2 ]]
377
-
378
- def dot (self , x , y ):
379
- return x [0 ] * y [0 ] + x [1 ] * y [1 ] + x [2 ] * y [2 ]
380
-
381
-
382
261
#
383
262
# different ways to load a set of dicom files:
384
263
# - Logic: relies on the same loading mechanism used
0 commit comments