Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add Image dict and pixel set/get Python interfaces #2039

Merged
merged 1 commit into from
Oct 6, 2020

Conversation

thewtex
Copy link
Member

@thewtex thewtex commented Oct 6, 2020

Provides a Python dictionary interface to image metadata, keys are
MetaDataDictionary entries along with 'origin', 'spacing', and
'direction' keys. The later reverse their order to be consistent with
the NumPy array index order resulting from array views of the image. The
entries can be accesses and set directly on the image, e.g.

print(image['0008|0008'])
image['origin'] = [4.0, 2.0, 2.0]

or a dictionary can be retrieved with:

meta_dict = dict(image)

For example:

In [3]: dict(image)
Out[3]:
{'0008|0005': 'ISO IR 100',
 '0008|0008': 'ORIGINAL\\PRIMARY\\AXIAL',
 '0008|0016': '1.2.840.10008.5.1.4.1.1.2',
 '0008|0018': '1.3.12.2.1107.5.8.99.484849.834848.79844848.2001082217554549',
 '0008|0020': '20010822',
 '0008|0021': '20010822',
 '0008|0022': '20010822',
 '0008|0023': '20010822',
 '0008|0030': '093652.000000 ',
 '0008|0031': '093652.000000 ',
 '0008|0032': '094028.820000 ',
 '0008|0033': '094335.532000 ',
 '0008|0040': '0',
 '0008|0041': 'IMA SPI ',
 '0008|0050': '                ',
 '0008|0060': 'CT',
 '0008|0070': 'SIEMENS ',
 '0008|0080': 'UNC HOSPITALS - BODY C    ',
 '0008|0090': '',
 '0008|1010': 'bodyct          ',
 '0008|1090': 'SOMATOM PLUS 4            ',
 '0010|0010': 'LIVER DONOR PHANTOM 2                                           ',
 '0010|0020': '2                                                               ',
 '0010|0030': '',
 '0010|0040': 'M ',
 '0018|0010': 'NONE    ',
 '0018|0015': 'BODY',
 '0018|0020': 'RM',
 '0018|0050': '003.000000E+00',
 '0018|0060': '000120',
 '0018|0090': '000500',
 '0018|1000': '                     20380',
 '0018|1020': 'VC10C   ',
 '0018|1110': '001005',
 '0018|1111': '000570',
 '0018|1120': '000.000000E+00',
 '0018|1130': '001.270000E+02',
 '0018|1140': 'CW',
 '0018|1150': '750 ',
 '0018|1151': '170 ',
 '0018|1152': '128 ',
 '0018|1170': '20',
 '0018|1190': '001.200000E+00',
 '0018|1200': '20010817',
 '0018|1201': '072320.877000 ',
 '0018|1210': '59 .10.AB50     ',
 '0018|5100': 'HFS ',
 '0020|000d': '1.3.12.2.1107.5.8.99.484849.834848.79844848.2001082213291053',
 '0020|000e': '1.3.12.2.1107.5.1.1.20380.52.36.9.22.8.2001.3                  ',
 '0020|0010': '000001',
 '0020|0011': '000004',
 '0020|0012': '000003',
 '0020|0013': '000052',
 '0020|0030': '-01.217578E+02\\-1.437578E+02\\-8.360000E+02',
 '0020|0032': '-122\\-144\\-836',
 '0020|0035': '001.000000E+00\\00.000000E+00\\00.000000E+00\\00.000000E+00\\01.000000E+00\\00.000000E+00',
 '0020|0037': '1\\0\\0\\0\\1\\0 ',
 '0020|0050': '-08.360000E+02',
 '0020|0052': '1.3.12.2.1107.5.1.1.20380.52.36.9.22.8.2001                    ',
 '0020|0070': 'PLANAR  ',
 '0020|0080': '',
 '0020|1040': '',
 '0020|1041': '-836',
 '0020|3100': '',
 '0020|3401': '',
 '0020|3402': '',
 '0020|3403': '',
 '0020|3404': '',
 '0020|3405': '',
 '0020|3406': '',
 '0020|5000': '',
 '0020|5002': '',
 '0028|0002': '1',
 '0028|0004': 'MONOCHROME2 ',
 '0028|0005': '2',
 '0028|0010': '512',
 '0028|0011': '512',
 '0028|0030': '004.843750E-01\\04.843750E-01',
 '0028|0040': 'RECT',
 '0028|0050': '',
 '0028|0100': '16',
 '0028|0101': '12',
 '0028|0102': '11',
 '0028|0103': '0',
 '0028|0200': '32736',
 '0028|1050': '000070\\-0700',
 '0028|1051': '000422\\02000',
 '0028|1052': '-01.024000E+03',
 '0028|1053': '001.000000E+00',
 'origin': array([-836., -144., -122.]),
 'spacing': array([1.      , 0.484375, 0.484375]),
 'direction': array([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])}

For non-string keys, they are passed to the NumPy array view so array views can be set and get with NumPy indexing syntax, e.g.

In [6]: image[0,:2,4] = [5,5]

In [7]: image[0,:4,4:6]
Out[7]:
NDArrayITKBase([[    5,  -997],
                [    5, -1003],
                [ -993,  -999],
                [ -996,  -994]], dtype=int16)

NumPy 1.15 or later is required to support the axis=None argument to
np.flip.

Copy link
Member

@aylward aylward left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outstanding! Nicely done!

Comment on lines +466 to +469
meta_keys = self.GetMetaDataDictionary().GetKeys()
# Ignore deprecated, legacy members that cause issues
result = list(filter(lambda k: not k.startswith('ITK_original'), meta_keys))
result.extend(['origin', 'spacing', 'direction'])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps assert that origin, spacing, and direction are not supplied by meta_keys = self.GetMetaDataDictionary().GetKeys(). If they were, their values there would get hidden, which would be hard to debug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth considering, but for simplicity, origin, spacing, direction keys also in the MetaDataDictionary should not be a valid, supported state. We do not want duplication, and the Origin, Spacing, and Direction ivars should be used for these.

Provides a Python dictionary interface to image metadata, keys are
MetaDataDictionary entries along with 'origin', 'spacing', and
'direction' keys. The later reverse their order to be consistent with
the NumPy array index order resulting from array views of the image. The
entries can be accesses and set directly on the image, e.g.

  print(image['0008|0008'])
  image['origin'] = [4.0, 2.0, 2.0]

or a dictionary can be retrieved with:

  meta_dict = dict(image)

For example:

```
In [3]: dict(image)
Out[3]:
{'0008|0005': 'ISO IR 100',
 '0008|0008': 'ORIGINAL\\PRIMARY\\AXIAL',
 '0008|0016': '1.2.840.10008.5.1.4.1.1.2',
 '0008|0018': '1.3.12.2.1107.5.8.99.484849.834848.79844848.2001082217554549',
 '0008|0020': '20010822',
 '0008|0021': '20010822',
 '0008|0022': '20010822',
 '0008|0023': '20010822',
 '0008|0030': '093652.000000 ',
 '0008|0031': '093652.000000 ',
 '0008|0032': '094028.820000 ',
 '0008|0033': '094335.532000 ',
 '0008|0040': '0',
 '0008|0041': 'IMA SPI ',
 '0008|0050': '                ',
 '0008|0060': 'CT',
 '0008|0070': 'SIEMENS ',
 '0008|0080': 'UNC HOSPITALS - BODY C    ',
 '0008|0090': '',
 '0008|1010': 'bodyct          ',
 '0008|1090': 'SOMATOM PLUS 4            ',
 '0010|0010': 'LIVER DONOR PHANTOM 2                                           ',
 '0010|0020': '2                                                               ',
 '0010|0030': '',
 '0010|0040': 'M ',
 '0018|0010': 'NONE    ',
 '0018|0015': 'BODY',
 '0018|0020': 'RM',
 '0018|0050': '003.000000E+00',
 '0018|0060': '000120',
 '0018|0090': '000500',
 '0018|1000': '                     20380',
 '0018|1020': 'VC10C   ',
 '0018|1110': '001005',
 '0018|1111': '000570',
 '0018|1120': '000.000000E+00',
 '0018|1130': '001.270000E+02',
 '0018|1140': 'CW',
 '0018|1150': '750 ',
 '0018|1151': '170 ',
 '0018|1152': '128 ',
 '0018|1170': '20',
 '0018|1190': '001.200000E+00',
 '0018|1200': '20010817',
 '0018|1201': '072320.877000 ',
 '0018|1210': '59 .10.AB50     ',
 '0018|5100': 'HFS ',
 '0020|000d': '1.3.12.2.1107.5.8.99.484849.834848.79844848.2001082213291053',
 '0020|000e': '1.3.12.2.1107.5.1.1.20380.52.36.9.22.8.2001.3                  ',
 '0020|0010': '000001',
 '0020|0011': '000004',
 '0020|0012': '000003',
 '0020|0013': '000052',
 '0020|0030': '-01.217578E+02\\-1.437578E+02\\-8.360000E+02',
 '0020|0032': '-122\\-144\\-836',
 '0020|0035': '001.000000E+00\\00.000000E+00\\00.000000E+00\\00.000000E+00\\01.000000E+00\\00.000000E+00',
 '0020|0037': '1\\0\\0\\0\\1\\0 ',
 '0020|0050': '-08.360000E+02',
 '0020|0052': '1.3.12.2.1107.5.1.1.20380.52.36.9.22.8.2001                    ',
 '0020|0070': 'PLANAR  ',
 '0020|0080': '',
 '0020|1040': '',
 '0020|1041': '-836',
 '0020|3100': '',
 '0020|3401': '',
 '0020|3402': '',
 '0020|3403': '',
 '0020|3404': '',
 '0020|3405': '',
 '0020|3406': '',
 '0020|5000': '',
 '0020|5002': '',
 '0028|0002': '1',
 '0028|0004': 'MONOCHROME2 ',
 '0028|0005': '2',
 '0028|0010': '512',
 '0028|0011': '512',
 '0028|0030': '004.843750E-01\\04.843750E-01',
 '0028|0040': 'RECT',
 '0028|0050': '',
 '0028|0100': '16',
 '0028|0101': '12',
 '0028|0102': '11',
 '0028|0103': '0',
 '0028|0200': '32736',
 '0028|1050': '000070\\-0700',
 '0028|1051': '000422\\02000',
 '0028|1052': '-01.024000E+03',
 '0028|1053': '001.000000E+00',
 'origin': array([-836., -144., -122.]),
 'spacing': array([1.      , 0.484375, 0.484375]),
 'direction': array([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])}
```

For non-string keys, they are passed to the NumPy array view so array views can be set and get with NumPy indexing syntax, e.g.

```
In [6]: image[0,:2,4] = [5,5]

In [7]: image[0,:4,4:6]
Out[7]:
NDArrayITKBase([[    5,  -997],
                [    5, -1003],
                [ -993,  -999],
                [ -996,  -994]], dtype=int16)
```

NumPy 1.15 or later is required to support the axis=None argument to
np.flip.
@thewtex thewtex merged commit ecc5ed9 into InsightSoftwareConsortium:master Oct 6, 2020
@thewtex thewtex deleted the metadata-dict branch October 6, 2020 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants