2
2
3
3
"""Convert DICOM dirs based on heuristic info
4
4
5
- This script uses DicomStack and mri_convert to convert DICOM directories.
5
+ This script uses the dcmstack package and dcm2niix tool to convert DICOM
6
+ directories or tarballs into collections of NIfTI files following pre-defined
7
+ heuristic(s).
6
8
7
9
It has multiple modes of operation
8
10
@@ -18,7 +20,7 @@ It has multiple modes of operation
18
20
DICOMs are sorted based on study UID, and layed out using specified heuristic
19
21
"""
20
22
21
- __version__ = '0.2 '
23
+ __version__ = '0.3 '
22
24
23
25
import argparse
24
26
from glob import glob
@@ -124,6 +126,8 @@ StudySessionInfo = namedtuple(
124
126
)
125
127
126
128
129
+ # TODO: RF to avoid package-level global structure, and be more friendly in
130
+ # case of refactoring of heudiconv into a proper Python package/module
127
131
class TempDirs (object ):
128
132
"""A helper to centralize handling and cleanup of dirs"""
129
133
@@ -296,23 +300,24 @@ def find_files(regex, topdir=curdir, exclude=None, exclude_vcs=True, dirs=False)
296
300
find_files .__doc__ %= (_VCS_REGEX ,)
297
301
298
302
299
- def group_dicoms_into_seqinfos (fl , flfilter = None , dcmfilter = None , grouping = 'studyUID' ):
303
+ def group_dicoms_into_seqinfos (
304
+ files , file_filter = None , dcmfilter = None , grouping = 'studyUID'
305
+ ):
300
306
"""Process list of dicoms and return seqinfo and file group
301
307
302
308
`seqinfo` contains per-sequence extract of fields from DICOMs which
303
309
will be later provided into heuristics to decide on filenames
304
310
305
311
Parameters
306
312
----------
307
- fl : list of str
313
+ files : list of str
308
314
List of files to consider
309
- flfilter : callable, optional
310
- Applied to each of fl . Should return True if file needs to be kept,
311
- False otherwise. Used to filter fl
315
+ file_filter : callable, optional
316
+ Applied to each item of filenames . Should return True if file needs to be
317
+ kept, False otherwise.
312
318
dcmfilter : callable, optional
313
- If called on dcm_data and returns True, it is used to set
314
- series_id
315
- grouping : str ('studyUID', 'accession_number') or None, optional
319
+ If called on dcm_data and returns True, it is used to set series_id
320
+ grouping : {'studyUID', 'accession_number', None}, optional
316
321
what to group by: studyUID or accession_number
317
322
318
323
Returns
@@ -328,24 +333,25 @@ def group_dicoms_into_seqinfos(fl, flfilter=None, dcmfilter=None, grouping='stud
328
333
raise ValueError ('I do not know how to group by {0}' .format (grouping ))
329
334
per_studyUID = grouping == 'studyUID'
330
335
per_accession_number = grouping == 'accession_number'
331
- lgr .info ("Analyzing %d dicoms" , len (fl ))
336
+ lgr .info ("Analyzing %d dicoms" , len (files ))
332
337
import dcmstack as ds
333
338
import dicom as dcm
334
339
335
340
groups = [[], []]
336
341
mwgroup = []
337
342
338
- studyUID = None # for sanity check that all DICOMs came from the same
339
- # "study". If not -- what is the use-case? (interrupted acquisition?)
340
- # and how would then we deal with series numbers
341
- # which would differ already
342
- if flfilter :
343
- nfl_before = len (fl )
344
- fl = list (filter (flfilter , fl ))
345
- nfl_after = len (fl )
343
+ studyUID = None
344
+ # for sanity check that all DICOMs came from the same
345
+ # "study". If not -- what is the use-case? (interrupted acquisition?)
346
+ # and how would then we deal with series numbers
347
+ # which would differ already
348
+ if file_filter :
349
+ nfl_before = len (files )
350
+ files = list (filter (file_filter , files ))
351
+ nfl_after = len (files )
346
352
lgr .info ('Filtering out {0} dicoms based on their filename' .format (
347
353
nfl_before - nfl_after ))
348
- for fidx , filename in enumerate (fl ):
354
+ for fidx , filename in enumerate (files ):
349
355
# TODO after getting a regression test check if the same behavior
350
356
# with stop_before_pixels=True
351
357
mw = ds .wrapper_from_data (dcm .read_file (filename , force = True ))
@@ -357,30 +363,29 @@ def group_dicoms_into_seqinfos(fl, flfilter=None, dcmfilter=None, grouping='stud
357
363
pass
358
364
359
365
try :
360
- studyUID_ = mw .dcm_data .StudyInstanceUID
366
+ file_studyUID = mw .dcm_data .StudyInstanceUID
361
367
except AttributeError :
362
- #import pdb; pdb.set_trace()
363
368
lgr .info ("File %s is missing any StudyInstanceUID" % filename )
364
- studyUID_ = None
369
+ file_studyUID = None
365
370
#continue
366
371
367
372
try :
368
373
series_id = (int (mw .dcm_data .SeriesNumber ),
369
374
mw .dcm_data .ProtocolName )
370
- studyUID_ = mw .dcm_data .StudyInstanceUID
375
+ file_studyUID = mw .dcm_data .StudyInstanceUID
371
376
372
377
if not per_studyUID :
373
378
# verify that we are working with a single study
374
379
if studyUID is None :
375
- studyUID = studyUID_
380
+ studyUID = file_studyUID
376
381
elif not per_accession_number :
377
- assert studyUID == studyUID_
382
+ assert studyUID == file_studyUID
378
383
except AttributeError as exc :
379
384
lgr .warning ('Ignoring %s since not quite a "normal" DICOM: %s' ,
380
385
filename , exc )
381
386
# not a normal DICOM -> ignore
382
387
series_id = (- 1 , 'none' )
383
- studyUID_ = None
388
+ file_studyUID = None
384
389
385
390
if not series_id [0 ] < 0 :
386
391
if dcmfilter is not None and dcmfilter (mw .dcm_data ):
@@ -403,7 +408,7 @@ def group_dicoms_into_seqinfos(fl, flfilter=None, dcmfilter=None, grouping='stud
403
408
series_id = (- 1 , mw .dcm_data .ProtocolName )
404
409
405
410
if per_studyUID :
406
- series_id = series_id + (studyUID_ ,)
411
+ series_id = series_id + (file_studyUID ,)
407
412
408
413
409
414
#print fidx, N, filename
@@ -413,13 +418,13 @@ def group_dicoms_into_seqinfos(fl, flfilter=None, dcmfilter=None, grouping='stud
413
418
#print idx, same, groups[idx][0]
414
419
if same :
415
420
# the same series should have the same study uuid
416
- assert mwgroup [idx ].dcm_data .get ('StudyInstanceUID' , None ) == studyUID_
421
+ assert mwgroup [idx ].dcm_data .get ('StudyInstanceUID' , None ) == file_studyUID
417
422
ingrp = True
418
423
if series_id [0 ] >= 0 :
419
424
series_id = (mwgroup [idx ].dcm_data .SeriesNumber ,
420
425
mwgroup [idx ].dcm_data .ProtocolName )
421
426
if per_studyUID :
422
- series_id = series_id + (studyUID_ ,)
427
+ series_id = series_id + (file_studyUID ,)
423
428
groups [0 ].append (series_id )
424
429
groups [1 ].append (idx )
425
430
@@ -445,7 +450,7 @@ def group_dicoms_into_seqinfos(fl, flfilter=None, dcmfilter=None, grouping='stud
445
450
# nothing to see here, just move on
446
451
continue
447
452
dcminfo = mw .dcm_data
448
- files = [fl [i ] for i , s in enumerate (groups [0 ]) if s == series_id ]
453
+ files = [files [i ] for i , s in enumerate (groups [0 ]) if s == series_id ]
449
454
# turn the series_id into a human-readable string -- string is needed
450
455
# for JSON storage later on
451
456
if per_studyUID :
@@ -1261,7 +1266,7 @@ def convert_dicoms(sid,
1261
1266
if dicoms :
1262
1267
seqinfo = group_dicoms_into_seqinfos (
1263
1268
dicoms ,
1264
- flfilter = getattr (heuristic , 'filter_files' , None ),
1269
+ file_filter = getattr (heuristic , 'filter_files' , None ),
1265
1270
dcmfilter = getattr (heuristic , 'filter_dicom' , None ),
1266
1271
grouping = None , # no groupping
1267
1272
)
@@ -1454,10 +1459,9 @@ def get_study_sessions(dicom_dir_template, files_opt, heuristic, outdir,
1454
1459
1455
1460
# sort all DICOMS using heuristic
1456
1461
# TODO: this one is not groupping by StudyUID but may be we should!
1457
- #import pdb; pdb.set_trace()
1458
1462
seqinfo_dict = group_dicoms_into_seqinfos (
1459
1463
files_ ,
1460
- flfilter = getattr (heuristic , 'filter_files' , None ),
1464
+ file_filter = getattr (heuristic , 'filter_files' , None ),
1461
1465
dcmfilter = getattr (heuristic , 'filter_dicom' , None ),
1462
1466
grouping = grouping )
1463
1467
@@ -1727,7 +1731,6 @@ def add_to_datalad(topdir, studydir, msg=None, bids=False):
1727
1731
mark_sensitive (ds , '*/*/anat' ) # within subj/ses
1728
1732
if dsh :
1729
1733
mark_sensitive (dsh ) # entire .heudiconv!
1730
- # import pdb; pdb.set_trace()
1731
1734
dsh .save (message = msg )
1732
1735
ds .save (message = msg , recursive = True , super_datasets = True )
1733
1736
0 commit comments