-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automatic conversion first pass. (#64)
- Loading branch information
Showing
4 changed files
with
218 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,5 @@ | ||
""" spec2nii - tool for conversion of various MRS data formats to NIFTI format. | ||
spec2nii converts the following formats to NIFTI files. | ||
Supporting SVS: | ||
Siemens "Twix" .dat format | ||
Siemens DICOM | ||
Philips SPAR/SDAT files | ||
GE p-files | ||
UIH DICOM | ||
LCModel RAW | ||
jMRUI text | ||
Plain text | ||
Supporting CSI/MRSI: | ||
Siemens DICOM | ||
UIH DICOM | ||
This module contains the main class to be called as a script (through the main function). | ||
Author: William Clarke <[email protected]> | ||
|
@@ -65,6 +50,12 @@ def add_common_parameters(subparser): | |
subparser.add_argument('--verbose', action='store_true') | ||
return subparser | ||
|
||
# Auto subcommand - heuristic ID of file type | ||
parser_auto = subparsers.add_parser('auto', help='Attempt automatic identification and conversion.') | ||
parser_auto.add_argument('file', help='file to convert', type=Path) | ||
parser_auto = add_common_parameters(parser_auto) | ||
parser_auto.set_defaults(func=self.auto) | ||
|
||
# Handle twix subcommand | ||
parser_twix = subparsers.add_parser('twix', help='Convert from Siemens .dat twix format.') | ||
parser_twix.add_argument('file', help='file to convert', type=Path) | ||
|
@@ -91,10 +82,10 @@ def add_common_parameters(subparser): | |
parser_dicom.set_defaults(func=self.dicom) | ||
|
||
# Handle rda subcommand | ||
parser_dicom = subparsers.add_parser('rda', help='Convert from Siemens spectroscopy .rda format.') | ||
parser_dicom.add_argument('file', help='file to convert', type=Path) | ||
parser_dicom = add_common_parameters(parser_dicom) | ||
parser_dicom.set_defaults(func=self.rda) | ||
parser_rda = subparsers.add_parser('rda', help='Convert from Siemens spectroscopy .rda format.') | ||
parser_rda.add_argument('file', help='file to convert', type=Path) | ||
parser_rda = add_common_parameters(parser_rda) | ||
parser_rda.set_defaults(func=self.rda) | ||
|
||
# Handle UIH DICOM subcommand | ||
parser_uih_dicom = subparsers.add_parser('uih', help='Convert from UIH DICOM format.') | ||
|
@@ -376,6 +367,94 @@ def validate_write(self, verbose): | |
else: | ||
raise self.NIfTIMRSWriteError(f'Output {out.name} in {out.parent} not found!') | ||
|
||
# Attempt automatic file type identification | ||
def auto(self, args): | ||
"""Attempt automatic file type identification and conversion | ||
Currently handles: Twix, RDA, SPAR/SDAT, GE pfile, DICOM | ||
""" | ||
from warnings import warn | ||
warn('Automatic conversion is an experimental feature. Please verify the output carefully.') | ||
|
||
# Twix format - ID by extension | ||
if args.file.suffix.lower() == '.dat': | ||
print("Attempting conversion as Siemens Twix (.dat), evalinfo = 'image'") | ||
setattr(args, 'evalinfo', 'image') | ||
setattr(args, 'quiet', False) | ||
setattr(args, 'view', False) | ||
for idx in range(5, 8): | ||
setattr(args, f'dim{idx}', None) | ||
setattr(args, f'tag{idx}', None) | ||
setattr(args, 'remove_os', False) | ||
self.twix(args) | ||
|
||
# Siemens RDA format - ID by extension | ||
elif args.file.suffix.lower() == '.rda': | ||
print('Attempting conversion as Siemens RDA (.rda)') | ||
self.rda(args) | ||
|
||
# Philips SPAR/SDAT format - ID by extension(s) | ||
elif args.file.suffix.lower() in ('.spar', '.sdat'): | ||
print('Attempting conversion as Philips SPAR/SDAT pair') | ||
if args.file.with_suffix('.SPAR').is_file()\ | ||
and args.file.with_suffix('.SDAT').is_file(): | ||
setattr(args, 'spar', args.file.with_suffix('.SPAR')) | ||
setattr(args, 'sdat', args.file.with_suffix('.SDAT')) | ||
else: | ||
raise Spec2niiError( | ||
'Unable to find both files in SPAR/SDAT pair.' | ||
'Please use manual selection of subcomands. ' | ||
'e.g. spec2nii philips ...') | ||
setattr(args, 'tags', ["DIM_DYN", None, None]) | ||
setattr(args, 'shape', None) | ||
setattr(args, 'special', None) | ||
self.philips(args) | ||
|
||
# Philips DATA/LIST - Reject bcause of unknown aux file format. | ||
elif args.file.suffix.lower() in ('.data', '.list'): | ||
raise Spec2niiError( | ||
'Automatic conversion not setup for data/list conversion. ' | ||
'Please use manual selection of subcomand. ' | ||
'e.g. spec2nii philips_dl ...') | ||
|
||
# GE pfile (.7) format - ID by extension | ||
elif args.file.suffix.lower() == '.7': | ||
self.ge(args) | ||
|
||
# If no matches assume DICOM - ID by loading file | ||
else: | ||
import pydicom as pdcm | ||
try: | ||
if args.file.is_dir(): | ||
files_in = \ | ||
sorted(args.file.rglob('*.IMA')) + \ | ||
sorted(args.file.rglob('*.ima')) + \ | ||
sorted(args.file.rglob('*.dcm')) | ||
file = pdcm.read_file(files_in[0]) | ||
else: | ||
file = pdcm.read_file(args.file) | ||
|
||
manufacturer = file.Manufacturer | ||
setattr(args, 'tag', None) | ||
if manufacturer.lower() == 'siemens': | ||
print('Attempting conversion as Siemens DICOM.') | ||
setattr(args, 'voi', False) | ||
self.dicom(args) | ||
elif manufacturer.lower() == 'uih': | ||
print('Attempting conversion as UIH DICOM.') | ||
self.uih_dicom(args) | ||
elif manufacturer.lower() == 'philips medical systems': | ||
print('Attempting conversion as Philips DICOM.') | ||
self.philips_dicom(args) | ||
else: | ||
raise Spec2niiError(f'Unknown DICOM manufacturer {manufacturer}.') | ||
# No sucessful ID as DICOM - fail at automatic load. | ||
except pdcm.errors.InvalidDicomError: | ||
raise Spec2niiError( | ||
'Unable to automatically identify file type. ' | ||
'Please use manual selection of subcomands. ' | ||
'e.g. spec2nii twix ..., spec2nii philips ...') | ||
|
||
# Start of the specific format handling functions. | ||
# Siemens twix (.dat) format | ||
def twix(self, args): | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
'''Tests automatic conversion routines. | ||
Copyright William Clarke, University of Oxford 2023 | ||
Subject to the BSD 3-Clause License. | ||
''' | ||
|
||
import subprocess | ||
from pathlib import Path | ||
|
||
test_base = Path(__file__).parent / 'spec2nii_test_data' | ||
testdata = { | ||
'twix': test_base / 'Siemens' / 'VEData' / 'Twix' / 'meas_MID00242_FID62747_svs_se_iso_tra_sat.dat', | ||
'rda': test_base / 'Siemens' / 'XAData' / 'XA20' / 'rda' / 'spct_002.MR.MRI-LAB Test_Dir.5.1.114540.rda', | ||
'spar': test_base / 'philips' / 'P1' / 'SV_PRESS_sh_6_2_raw_act.SPAR', | ||
'data_list': test_base / 'philips' / 'hyper' / 'raw_226.list', | ||
'ge_p': test_base / 'ge' / 'pFiles' / 'svMRS' / 'P03072.7', | ||
'dicom_siemens': test_base / 'Siemens' / 'XAData' / 'XA20' / 'DICOM' / '26516628.dcm', | ||
'dicom_uih': test_base / 'UIH' / 'mrs_data' / 'dicom' / 'svs_press_te144_SVS_801' / '00000001.dcm', | ||
'dicom_philips': test_base / 'philips' / 'DICOM' / 'SV_phantom_center' / 'IM-0018-0002-0001.dcm'} | ||
|
||
|
||
def test_auto_twix(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['twix'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
|
||
def test_auto_rda(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['rda'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
|
||
def test_auto_sdat_spar(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['spar'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
|
||
def test_auto_ge(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['ge_p'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
|
||
def test_auto_siemens_dicom(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_siemens'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_siemens'].parent, | ||
'-o', tmp_path, | ||
'-f', 'test_dir']) | ||
|
||
assert (tmp_path / 'test_dir.nii.gz').is_file() | ||
|
||
|
||
def test_auto_uih_dicom(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_uih'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_uih'].parent, | ||
'-o', tmp_path, | ||
'-f', 'test_dir']) | ||
|
||
assert (tmp_path / 'test_dir.nii.gz').is_file() | ||
|
||
|
||
def test_auto_philips_dicom(tmp_path): | ||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_philips'], | ||
'-o', tmp_path, | ||
'-f', 'test_file']) | ||
|
||
assert (tmp_path / 'test_file.nii.gz').is_file() | ||
|
||
subprocess.run([ | ||
'spec2nii', 'auto', | ||
testdata['dicom_philips'].parent, | ||
'-o', tmp_path, | ||
'-f', 'test_dir']) | ||
|
||
assert (tmp_path / 'test_dir.nii.gz').is_file() |