@@ -20,7 +20,7 @@ It has multiple modes of operation
20
20
DICOMs are sorted based on study UID, and layed out using specified heuristic
21
21
"""
22
22
23
- __version__ = '0.3 '
23
+ __version__ = '0.4 '
24
24
25
25
import argparse
26
26
from glob import glob
@@ -245,7 +245,8 @@ def json_dumps_pretty(j, indent=2, sort_keys=True):
245
245
# no spaces after [
246
246
js_ = re .sub ('\[ ' , '[' , js_ )
247
247
j_ = json .loads (js_ )
248
- assert (j == j_ )
248
+ # Removed assert as it does not do any floating point comparison
249
+ #assert(j == j_)
249
250
return js_
250
251
251
252
@@ -313,6 +314,14 @@ def find_files(regex, topdir=curdir, exclude=None, exclude_vcs=True, dirs=False)
313
314
find_files .__doc__ %= (_VCS_REGEX ,)
314
315
315
316
317
+ def is_readonly (path ):
318
+ """Return True if it is a fully read-only file (dereferences the symlink)
319
+ """
320
+ # get current permissions
321
+ perms = stat .S_IMODE (os .lstat (os .path .realpath (path )).st_mode )
322
+ return not bool (perms & ALL_CAN_WRITE ) # should be true if anyone is allowed to write
323
+
324
+
316
325
def set_readonly (path , read_only = True ):
317
326
"""Make file read only or writeable while preserving "access levels"
318
327
@@ -453,11 +462,9 @@ def group_dicoms_into_seqinfos(
453
462
series_id = series_id + (file_studyUID ,)
454
463
455
464
456
- #print fidx, N, filename
457
465
ingrp = False
458
466
for idx in range (len (mwgroup )):
459
467
same = mw .is_same_series (mwgroup [idx ])
460
- #print idx, same, groups[idx][0]
461
468
if same :
462
469
# the same series should have the same study uuid
463
470
assert mwgroup [idx ].dcm_data .get ('StudyInstanceUID' , None ) == file_studyUID
@@ -792,7 +799,7 @@ def get_dicom_series_time(dicom_list):
792
799
import calendar
793
800
import dicom as dcm
794
801
795
- dcm = dcm .read_file (dicom_list [0 ], stop_before_pixels = True )
802
+ dcm = dcm .read_file (dicom_list [0 ], stop_before_pixels = True , force = True )
796
803
dcm_date = dcm .SeriesDate # YYYYMMDD
797
804
dcm_time = dcm .SeriesTime # HHMMSS.MICROSEC
798
805
dicom_time_str = dcm_date + dcm_time .split ('.' , 1 )[0 ] # YYYYMMDDHHMMSS
@@ -1017,7 +1024,7 @@ def get_formatted_scans_key_row(item):
1017
1024
1018
1025
"""
1019
1026
dcm_fn = item [- 1 ][0 ]
1020
- mw = ds .wrapper_from_data (dcm .read_file (dcm_fn , stop_before_pixels = True ))
1027
+ mw = ds .wrapper_from_data (dcm .read_file (dcm_fn , stop_before_pixels = True , force = True ))
1021
1028
# we need to store filenames and acquisition times
1022
1029
# parse date and time and get it into isoformat
1023
1030
date = mw .dcm_data .ContentDate
@@ -1026,7 +1033,12 @@ def get_formatted_scans_key_row(item):
1026
1033
acq_time = datetime .strptime (td , '%H%M%S%Y%m%d' ).isoformat ()
1027
1034
# add random string
1028
1035
randstr = '' .join (map (chr , sample (k = 8 , population = range (33 , 127 ))))
1029
- row = [acq_time , mw .dcm_data .PerformingPhysicianName , randstr ]
1036
+ # Catch AttributeError if PerformingPhysicianName info is missing
1037
+ try :
1038
+ perfphys = mw .dcm_data .PerformingPhysicianName
1039
+ except AttributeError :
1040
+ perfphys = 'n/a'
1041
+ row = [acq_time , perfphys , randstr ]
1030
1042
# empty entries should be 'n/a'
1031
1043
# https://github.com/dartmouth-pbs/heudiconv/issues/32
1032
1044
row = ['n/a' if not str (e ) else e for e in row ]
@@ -1075,8 +1087,9 @@ def _find_subj_ses(f_name):
1075
1087
# we will allow the match at either directories or within filename
1076
1088
# assuming that bids layout is "correct"
1077
1089
regex = re .compile ('sub-(?P<subj>[a-zA-Z0-9]*)([/_]ses-(?P<ses>[a-zA-Z0-9]*))?' )
1078
- res = regex .search (f_name ).groupdict ()
1079
- return res .get ('subj' ), res .get ('ses' , None )
1090
+ regex_res = regex .search (f_name )
1091
+ res = regex_res .groupdict () if regex_res else {}
1092
+ return res .get ('subj' , None ), res .get ('ses' , None )
1080
1093
1081
1094
1082
1095
def save_scans_key (item , bids_files ):
@@ -1103,6 +1116,13 @@ def save_scans_key(item, bids_files):
1103
1116
f_name = f_name .replace ('json' , 'nii.gz' )
1104
1117
rows [f_name ] = get_formatted_scans_key_row (item )
1105
1118
subj_ , ses_ = _find_subj_ses (f_name )
1119
+ if not subj_ :
1120
+ lgr .warning (
1121
+ "Failed to detect fullfilled BIDS layout. "
1122
+ "No scans.tsv file(s) will be produced for %s" ,
1123
+ ", " .join (bids_files )
1124
+ )
1125
+ return
1106
1126
if subj and subj_ != subj :
1107
1127
raise ValueError (
1108
1128
"We found before subject %s but now deduced %s from %s"
@@ -1167,10 +1187,14 @@ def tuneup_bids_json_files(json_files):
1167
1187
except IOError as exc :
1168
1188
lgr .error ("Failed to open magnitude file: %s" , exc )
1169
1189
1170
- # might have been made R/O already
1171
- set_readonly (json_phasediffname , False )
1190
+ # might have been made R/O already, but if not -- it will be set
1191
+ # only later in the pipeline, so we must not make it read-only yet
1192
+ was_readonly = is_readonly (json_phasediffname )
1193
+ if was_readonly :
1194
+ set_readonly (json_phasediffname , False )
1172
1195
json .dump (json_ , open (json_phasediffname , 'w' ), indent = 2 )
1173
- set_readonly (json_phasediffname )
1196
+ if was_readonly :
1197
+ set_readonly (json_phasediffname )
1174
1198
1175
1199
# phasediff one should contain two PhaseDiff's
1176
1200
# -- one for original amplitude and the other already replicating what is there
@@ -1225,23 +1249,6 @@ def embed_metadata_from_dicoms(converter, is_bids, item_dicoms, outname,
1225
1249
cwd = os .getcwd ()
1226
1250
lgr .debug ("Embedding into %s based on dicoms[0]=%s for nifti %s" , scaninfo , item_dicoms [0 ], outname )
1227
1251
try :
1228
- """
1229
- Ran into
1230
- INFO: Executing node embedder in dir: /tmp/heudiconvdcm2W3UQ7/embedder
1231
- ERROR: Embedding failed: [Errno 13] Permission denied: '/inbox/BIDS/tmp/test2-jessie/Wheatley/Beau/1007_personality/sub-sid000138/fmap/sub-sid000138_3mm_run-01_phasediff.json'
1232
- while
1233
- HEUDICONV_LOGLEVEL=WARNING time bin/heudiconv -f heuristics/dbic_bids.py -c dcm2niix -o /inbox/BIDS/tmp/test2-jessie --bids --datalad /inbox/DICOM/2017/01/28/A000203
1234
-
1235
- so it seems that there is a filename collision so it tries to save into the same file name
1236
- and there was a screw up for that A
1237
-
1238
- /mnt/btrfs/dbic/inbox/DICOM/2017/01/28/A000203
1239
- StudySessionInfo(locator='Wheatley/Beau/1007_personality', session=None, subject='sid000138') 16 sequences
1240
- StudySessionInfo(locator='Wheatley/Beau/1007_personality', session=None, subject='a000203') 2 sequences
1241
-
1242
-
1243
- in that one though
1244
- """
1245
1252
if global_options ['overwrite' ] and os .path .lexists (scaninfo ):
1246
1253
# TODO: handle annexed file case
1247
1254
if not os .path .islink (scaninfo ):
@@ -1392,7 +1399,8 @@ def convert_dicoms(sid,
1392
1399
with_prov = with_prov ,
1393
1400
is_bids = is_bids ,
1394
1401
sourcedir = sourcedir ,
1395
- outdir = tdir )
1402
+ outdir = tdir ,
1403
+ min_meta = min_meta )
1396
1404
1397
1405
if is_bids :
1398
1406
if seqinfo :
0 commit comments