Usage

Matlab

Read and write raw scanner files as nifti + json

BIDS requires nifti files and json. While json can be writen be hand, this is more convenient to populate them as one reads data. One issue is that some information is not encoded in ecat/dicom headers and thus needs to be created otherwise.

Using matlab code

To simplify the curation of json files, one uses the get_pet_metadata.m function. This function takes as arguments the scanner info (thus loading the relevant *parameters.txt file) and also need some manual input related to the injected tracer.

Feel free to reach out if you have an issue with your scanner files, we can help.

The simplest way to convert DICOM files is to call dcm2niix4pet.m which wraps around dcm2niix. Assuming dcm2niix is present in your environment, Matlab will call it to convert your data to nifti and json - and the wrapper function will additionally edit the json file. Arguments in are the dcm folder(s) in, the metadata as a structure (using the get_pet_metadata.m function for instance) and possibly options as per dcm2niix.

Note for windows user: edit the dcm2niix4pet.m line 51 to indicate where is the .exe function located

meta = get_pet_metadata('Scanner','SiemensBiograph','TimeZero','ScanStart',
'TracerName','CB36','TracerRadionuclide','C11', 'ModeOfAdministration', 'infusion',
'SpecificRadioactivity', 605.3220,'InjectedMass', 1.5934,
'MolarActivity', 107.66, 'InstitutionName','Rigshospitalet, NRU, DK',
'AcquisitionMode','list mode','ImageDecayCorrected','true',
'ImageDecayCorrectionTime' ,0,'ReconMethodName','OP-OSEM',
'ReconMethodParameterLabels',{'subsets','iterations'},
'ReconMethodParameterUnits',{'none','none'}, 'ReconMethodParameterValues',[21 3],
'ReconFilterType','XYZGAUSSIAN', 'ReconFilterSize',2,
'AttenuationCorrection','CT-based attenuation correction');

dcm2niix4pet(dcmfolder,meta,'o','mynewfolder');

The get_pet_metadata function can be called in a much simpler way if you have a *parameters.txt seating on disk next to this function. The call would then looks like:

meta = get_pet_metadata('Scanner','SiemensBiograph','TimeZero','ScanStart',
'TracerName','CB36', 'TracerRadionuclide','C11', 'ModeOfAdministration',
'infusion','SpecificRadioactivity', 605.3220, 'InjectedMass', 1.5934,
'MolarActivity', 107.66);

dcm2niix4pet(dcmfolder,meta,'o','mynewfolder');

Alternatively, you could have data already converted to nifti and json, and you need to update the json file. This can be done 2 ways:

1. Use the updatejsonpetfile.m function. Arguments in are the json file to update and metadata to add as a structure (using a get_metadata.m function for instance) and possibly a dicom file to check additional fields. This is show below for data from the biograph.

jsonfilename = fullfile(pwd,'DBS_Gris_13_FullCT_DBS_Az_2mm_PRR_AC_Images_20151109090448_48.json')
# your SiemensBiographparameters.txt file is stored next to get_pet_metadata.m

metadata = get_pet_metadata('Scanner','SiemensBiograph','TimeZero','ScanStart',
'TracerName','AZ10416936','TracerRadionuclide','C11',
'ModeOfAdministration','bolus','InjectedRadioactivity', 605.3220,
'InjectedMass', 1.5934,'MolarActivity', 107.66);

dcminfo = dicominfo('DBSGRIS13.PT.PETMR_NRU.48.13.2015.11.11.14.03.16.226.61519201.dcm');
status = updatejsonpetfile(jsonfilename,metadata,dcminfo)``
  1. Add the metadata ‘manually’ to the json file, shown below for GE Advance data.

metadata1 = jsondecode(textread(myjsonfile.json)); % or use jsonread from the matlab BIDS library

%your GEAdvance.txt file is stored next to get_pet_metadata.m

metadata2 = get_pet_metadata('Scanner', 'GEAdvance','TimeZero','XXX','TracerName','DASB','TracerRadionuclide','C11',
'ModeOfAdministration','bolus', 'InjectedRadioactivity', 605.3220,'InjectedMass', 1.5934,'MolarActivity', 107.66);

metadata  = [metadata2;metadata1];
jsonwrite('mynewjsonfile.json'],metadata)

Converting ecat files

If you have ecat (.v) instead of dicom (.dcm), we have build a dedicated converter. Arguments in are the file to convert and some metadata as a structure (using the get_pet_metadata.m function for instance). This is shown below for HRRT data.

Your SiemensHRRT.txt file is stored next to get_pet_metadata.m

metadata = get_pet_metadata('Scanner','SiemensHRRT','TimeZero','XXX',
'TracerName','DASB','TracerRadionuclide', 'C11',
'ModeOfAdministration','bolus', 'InjectedRadioactivity',605.3220,
'InjectedMass', 1.5934,'MolarActivity', 107.66);

ecat2nii({full_file_name},{metadata})

See the documentation for further details on ecat conversion

Python

The python library is available for use on Posix systems (OSX and Linux) and Windows. Posix installation requires that dcm2niix be installed and findable on the system path. Windows requires the user to provide the path to dcm2niix within an environment variable in a config file.

For more information on how to install dcm2niix see here.

If successfully installed you should have access to 2 command line tools, check to see if they are available via your terminal/commandline by typing the following into your cmd window:

  • dcm2niix4pet

  • ecatpet2bids

You should see the following afterwards:

Microsoft Windows [Version 10.0.19042.2006]
(c) Microsoft Corporation. All rights reserved.

H:\>dcm2niix4pet
usage: dcm2niix4pet [-h] [--metadata-path METADATA_PATH] [--translation-script-path TRANSLATION_SCRIPT_PATH]
                    [--destination-path DESTINATION_PATH] [--kwargs [KWARGS ...]] [--silent SILENT] [--show-examples]
                    [--set-dcm2niix-path SET_DCM2NIIX_PATH]
                    [folder]

H:\>ecatpet2bids
usage: ecat_cli.py [-h] [--affine] [--convert] [--dump] [--json] [--nifti file_name] [--subheader] [--sidecar]
                   [--kwargs [KWARGS ...]] [--scannerparams [SCANNERPARAMS ...]] [--directory_table]
                   [--show-examples]
                   [ecat_file]

ecatpet2bids for converting ecat data into nii & json

# our ecat conversion library should be available via the following
ecatpet2bids -h
usage: ecatpet2bids [-h] [--affine] [--convert] [--dump] [--json] [--nifti file_name] [--subheader] [--sidecar] [--kwargs [KWARGS ...]] [--scannerparams [SCANNERPARAMS ...]] [--directory_table]
                ecat_file

positional arguments:
  ecat_file             Ecat image to collect info from.

optional arguments:
  -h, --help            show this help message and exit
  --affine, -a          Show affine matrix
  --convert, -c         If supplied will attempt conversion.
  --dump, -d            Dump information in Header
  --json, -j            Output header and subheader info as JSON to stdout, overrides all other options
  --nifti file_name, -n file_name
                        Name of nifti output file
  --subheader, -s       Display subheaders
  --sidecar             Output a bids formatted sidecar for pairing witha nifti.
  --kwargs [KWARGS ...], -k [KWARGS ...]
                        Include additional values int the nifti sidecar json or override values extracted from the supplied nifti. e.g. including `--kwargs TimeZero='12:12:12'` would override the
                        calculated TimeZero. Any number of additional arguments can be supplied after --kwargs e.g. `--kwargs BidsVariable1=1 BidsVariable2=2` etc etc.
  --scannerparams [SCANNERPARAMS ...]
                        Loads saved scanner params from a configuration file following --scanner-params/-s if this option is used without an argument this cli will look for any scanner parameters file
                        in the directory with the name *parameters.txt from which this cli is called.
  --directory_table, -t
                        Collect table/array of ECAT frame byte location map

For converting dicom to BIDS use dcm2niix4pet via:

dcm2niix4pet -h
usage: dcm2niix4pet [-h] [--metadata-path METADATA_PATH] [--translation-script-path TRANSLATION_SCRIPT_PATH] [--destination-path DESTINATION_PATH] [--kwargs [KWARGS ...]] [--silent SILENT]
                [--write-template-script]
                folder

positional arguments:
  folder                Folder path containing imaging data

optional arguments:
  -h, --help            show this help message and exit
  --metadata-path METADATA_PATH, -m METADATA_PATH
                        Path to metadata file for scan
  --translation-script-path TRANSLATION_SCRIPT_PATH, -t TRANSLATION_SCRIPT_PATH
                        Path to a script written to extract and transform metadata from a spreadsheet to BIDS compliant text files (tsv and json)
  --destination-path DESTINATION_PATH, -d DESTINATION_PATH
                        Destination path to send converted imaging and metadata files to. If omitted defaults to using the path supplied to folder path. If destination path doesn't exist an attempt to
                        create it will be made.
  --kwargs [KWARGS ...], -k [KWARGS ...]
                        Include additional values int the nifti sidecar json or override values extracted from the supplied nifti. e.g. including `--kwargs TimeZero='12:12:12'` would override the
                        calculated TimeZero. Any number of additional arguments can be supplied after --kwargs e.g. `--kwargs BidsVariable1=1 BidsVariable2=2` etc etc.
  --silent SILENT, -s SILENT
                        Display missing metadata warnings and errorsto stdout/stderr

Using pypet2bids

Pypet2bids is primarily designed to run as a command line utility, design choice was made for 2 purposes:

  1. to provide a universal interface (API) so the library is operable with any scripting or programming language

  2. keeping the usage as simple as possible, use of this library is as simple as install -> run command

Additionally, one has access to the underlying python methods and classes if one wishes to use this library from within a Python environment.


Command line usage:

In the most simple use case one can convert a folder full of dicoms into a NIFTI

dcm2niix4pet /folder/with/PET/dicoms/ -d /folder/with/PET/nifti_jsons

However, more often than not the information required to create a valid PET BIDS nifti and json isn’t present w/ in the dicom headers of the PET Image files.

Often additional radiological information will need to be passed to pypet2bids in addition to PET imaging data. Passing this data can be done in a number of increasingly complex ways. The simplest method to pass on information is directly at the command line when calling either dcm2niix4pet or ecatpet2bids. Both of these tools accept additional arguments via key pair’s separated by the equals sign =. This functionality is designed to mirror that of the Matlab code.

Some extra values in the case of this Siemens Biograph would look like the following:

dcm2niix4pet OpenNeuroPET-Phantoms/source/SiemensBiographPETMR-NRU --kwargs
TimeZero=ScanStart
Manufacturer=Siemens
ManufacturersModelName=Biograph InstitutionName="Rigshospitalet, NRU, DK"
BodyPart=Phantom
Units=Bq/mL
TracerName=none
TracerRadionuclide=F18
InjectedRadioactivity=81.24
SpecificRadioactivity=13019.23
ModeOfAdministration=infusion
FrameTimesStart=0
AcquisitionMode="list mode"
ImageDecayCorrected=true
ImageDecayCorrectionTime=0
AttenuationCorrection=MR-corrected
FrameDuration=300
FrameTimesStart=0

And similarly, extra key pair values can be passed to ecatpet2bids:

ecatpet2bids OpenNeuroPET-Phantoms/source/SiemensHRRT-NRU/XCal-Hrrt-2022.04.21.15.43.05_EM_3D.v
--convert
--kwargs
TimeZero=ScanStart
Manufacturer=Siemens
ManufacturersModelName=HRRT
InstitutionName="Rigshospitalet, NRU, DK"
BodyPart=Phantom
Units=Bq/mL
TracerName=none
TracerRadionuclide=F18
InjectedRadioactivity=81.24
SpecificRadioactivity=13019.23
ModeOfAdministration=infusion
AcquisitionMode="list mode"
ImageDecayCorrected=true
ImageDecayCorrectionTime=0
AttenuationCorrection="10-min transmission scan"

An additional method to Extract/inject information at the time of conversion involves the use of a spreadsheet. By including a spreadsheet file (tsv, xlsx, etc) that has been formatted like the following:

_images/image_example_bids_spreadsheet.png

Then point the optional metadatapath flag at the spreadsheet location:

dcm2niix4pet /folder/containing/PET/dicoms/
--destination /folder/containing/PET/nifti_jsons
--metadatapath /file/PET_metadata.xlsx

Development and Testing

PET2BIDS makes use of both unit and integration tests to ensure that the code is working as expected. Phantom, synthetic imaging, and blood test data can be retrieved directly from either this repository or via the url contained within the collectphantoms make command in the top level Makefile. Tests are written with relative paths to the test data contained in several directories within this repository:

  1. metadata/

  2. ecat_validation/

  3. tests/

  4. OpenNeuroPET-Phantoms/ (this last directory needs be downloaded separately with the make commands collectphantoms and unzipphantoms)

The unit tests for Python can be found in the tests folder and are run using the pytest library. Python tests are best run with poetry run:

poetry run pytest test/<test_file_name>.py

The matlab unit tests can be found in the matlab/unit_tests folder and are run in Matlab after adding this repository and its contents to you Matlab path.

addpath('path/to/PET2BIDS/matlab')
addpath('path/to/PET2BIDS/matlab/unit_tests')

Integration tests are run using the make testphantoms command. However, running the Matlab integration tests requires the user add the script located in OpenNeuroPET-Phantoms/code/matlab_conversions.m to their Matlab path.

To emulate the CI/CD pipeline, the following commands can be run locally in the following order:

make testphantoms
make testpython

Documentation for Read the Docs can be built and previewed locally using the following command:

make html

The output of the build can be found at docs/_build/html/ and previewed by opening any of the html files contained therein with a web browser. Chances are good if you’re able to build with no errors locally than any changes you make to the documentation rst files will be built successfully on Read the Docs, but not guaranteed.

Working with existing NiFTi and JSON files

PET2BIDS is primarily designed to work with DICOM and ECAT files, but it can also be used to add or update sidecar json metadata after conversion to NiFTi (whether by PET2BIDS or some other tool). This can be done using the updatejsonpetfile.m function in Matlab or the updatepetjson command line tool in Python. Additionally, if one has dicom or ecat files available to them they can use updatepetjsonfromdicom or updatepetjsonfromecat respectively instead of updatepetjson or updatejsonpetfile.m.

Disclaimer: Again, we strongly encourage users to use PET2BIDS or dcm2niix to convert their data from dicom or ecat into nifti. We have observed that nifti’s not produced by dcm2niix are often missing dicom metadata such as frame timing or image orientation. Additionally some non-dcm2niix nifti’s contain extra singleton dimensions or extended but undefined main image headers.

Examples of using the command line tools can be seen below:

updatepetjson /path/to/nifti.nii /path/to/json.json --kwargs TimeZero=12:12:12
updatepetjsonfromdicom /path/to/dicom.dcm /path/to/json.json --kwargs TimeZero=12:12:12
updatepetjsonfromecat /path/to/ecat.v /path/to/json.json --kwargs TimeZero=12:12:12
# run matlab to get this output
>> jsonfilename = fullfile(pwd,'DBS_Gris_13_FullCT_DBS_Az_2mm_PRR_AC_Images_20151109090448_48.json');
>> metadata = get_SiemensBiograph_metadata('TimeZero','ScanStart','tracer','AZ10416936','Radionuclide','C11',
   ... 'ModeOfAdministration','bolus','Radioactivity', 605.3220,'InjectedMass', 1.5934,'MolarActivity', 107.66);
>> dcminfo = dicominfo('DBSGRIS13.PT.PETMR_NRU.48.13.2015.11.11.14.03.16.226.61519201.dcm');
>> status = updatejsonpetfile(jsonfilename,metadata,dcminfo);