Skip to content

Commit

Permalink
Enh: Anonymisation at runtime (#113)
Browse files Browse the repository at this point in the history
* Enable anon as a command line flag

* Add help to anon option.
  • Loading branch information
wtclarke authored Oct 10, 2023
1 parent 4cd3ec5 commit e711d52
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
This document contains the Spec2nii release history in reverse chronological order.

0.7.1 (WIP)
--------------------------------
- The --anon flag can be passed with any call to anonymise after writing files.

0.7.0 (Saturday 5th August 2023)
--------------------------------
- Fixed a bug in Philips Classic DICOM orientations (supplementing the fixes to Enhanced DICOM in `0.6.11`)
Expand Down
44 changes: 30 additions & 14 deletions spec2nii/anonymise.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
from nifti_mrs.hdr_ext import Hdr_Ext


def anon_nifti_mrs(args):
"""Function for anonymising input NIfTI-MRS files.
def anon_nifti_mrs(nifti_mrs_img, extra_keys=None, verbose=False):
"""Function for anonymising an existing nifti-mrs object
:param args: Command line arguments parsed in spec2nii.py
:return: List of anonymised images
:rtype: [nib.nifti2.Nifti2Image,]
:return: List of output names
:rtype: [str,]
:param nifti_mrs_img: NIfTI-MRS object to anonymise
:type nifti_mrs_img: nifti_mrs.nifti_mrs.NIFTI_MRS
:param extra_keys: List of keys to explicitly remove, defaults to None
:type extra_keys: List, optional
:param verbose: Print verbose output, defaults to False
:type verbose: bool, optional
:return: Anonymised NIfTI-MRS object
:rtype: nifti_mrs.nifti_mrs.NIFTI_MRS
"""
# Load data
nifti_mrs_img = NIFTI_MRS(args.file)

# Extract header extension
hdr_ext = nifti_mrs_img.hdr_ext.to_dict()
Expand All @@ -38,8 +39,8 @@ def iter_json(in_dict):
removed = {}
for key, value in in_dict.items():
# Explicitly set on command line
if args.remove\
and key in args.remove:
if extra_keys\
and key in extra_keys:
removed.update({key: value})
# Standard defined
elif key in standard_defined:
Expand All @@ -65,24 +66,39 @@ def iter_json(in_dict):

anon_hdr, removed_dict = iter_json(hdr_ext)

if args.verbose:
if verbose:
pp = pprint.PrettyPrinter(indent=4)
print('\nThe following keys were removed:')
pp.pprint(removed_dict)
print('\nThe following keys were kept:')
pp.pprint(anon_hdr)

# Make new NIfTI-MRS image
anon_out = gen_nifti_mrs_hdr_ext(
return gen_nifti_mrs_hdr_ext(
nifti_mrs_img[:],
nifti_mrs_img.dwelltime,
Hdr_Ext.from_header_ext(anon_hdr),
affine=nifti_mrs_img.getAffine('voxel', 'world'))


def anon_file(args):
"""Function for anonymising NIfTI-MRS files stored on drive.
:param args: Command line arguments parsed in spec2nii.py
:return: List of anonymised images
:rtype: [nib.nifti2.Nifti2Image,]
:return: List of output names
:rtype: [str,]
"""

# Process output name.
if args.fileout:
fname_out = [args.fileout, ]
else:
fname_out = [args.file.with_suffix('').with_suffix('').name, ]

return [anon_out, ], fname_out
return [
anon_nifti_mrs(
NIFTI_MRS(args.file),
verbose=args.verbose,
extra_keys=args.remove), ], fname_out
21 changes: 17 additions & 4 deletions spec2nii/spec2nii.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ def add_common_parameters(subparser):
help="Override SpectrometerFrequency field with input(s). Input in MHz.")
subparser.add_argument("--override_dwelltime", type=float,
help="Override dwell time field with input. Input in seconds.")
subparser.add_argument(
'--anon',
action='store_true',
help="Create file without sensitive metadata. For greater control use spec2nii anon.")
subparser.add_argument('--verbose', action='store_true')

return subparser

# Auto subcommand - heuristic ID of file type
Expand Down Expand Up @@ -234,7 +239,8 @@ def add_common_parameters(subparser):
nifti1=False,
override_nucleus=None,
override_frequency=None,
override_dwelltime=None)
override_dwelltime=None,
anon=False)

parser_dump = subparsers.add_parser('dump', help='Dump contents of headers from existing NIfTI-MRS file.')
parser_dump.add_argument('file', help='NIfTI-MRS file', type=Path)
Expand Down Expand Up @@ -264,7 +270,8 @@ def add_common_parameters(subparser):
override_nucleus=None,
override_frequency=None,
override_dwelltime=None,
verbose=False)
verbose=False,
anon=False)

if len(sys.argv) == 1:
parser.print_usage(sys.stderr)
Expand All @@ -284,6 +291,12 @@ def add_common_parameters(subparser):

if self.imageOut:
self.implement_overrides(args)

if args.anon:
from spec2nii.anonymise import anon_nifti_mrs
for idx, nifti_mrs_img in enumerate(self.imageOut):
self.imageOut[idx] = anon_nifti_mrs(nifti_mrs_img, verbose=args.verbose)

self.validate_output()
self.write_output(args.json, args.nifti1)
self.validate_write(args.verbose)
Expand Down Expand Up @@ -641,8 +654,8 @@ def varian(self, args):

# Anonymise function
def anon(self, args):
from spec2nii.anonymise import anon_nifti_mrs
self.imageOut, self.fileoutNames = anon_nifti_mrs(args)
from spec2nii.anonymise import anon_file
self.imageOut, self.fileoutNames = anon_file(args)

# Dump function
@staticmethod
Expand Down
23 changes: 23 additions & 0 deletions tests/test_anon.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,26 @@ def test_anon(tmp_path):
assert 'PatientName' not in hdr_ext_a
assert 'PatientSex' not in hdr_ext_a
assert 'RepetitionTime' not in hdr_ext_a


def test_inline_anon(tmp_path):
# Convert twix
subprocess.check_call(['spec2nii', 'twix',
'-e', 'image',
'-f', 'original',
'-o', tmp_path,
'-j', str(data_path),
'--anon',
'--verbose'])

img_o = read_nifti_mrs(tmp_path / 'original.nii.gz')

hdr_ext_codes = img_o.header.extensions.get_codes()
hdr_ext_o = json.loads(img_o.header.extensions[hdr_ext_codes.index(44)].get_content())

assert 'SpectrometerFrequency' in hdr_ext_o
assert 'ResonantNucleus' in hdr_ext_o
assert 'dim_5' in hdr_ext_o
assert 'OriginalFile' not in hdr_ext_o
assert 'PatientName' not in hdr_ext_o
assert 'PatientDoB' not in hdr_ext_o

0 comments on commit e711d52

Please sign in to comment.